Compare commits

...

48 Commits

Author SHA1 Message Date
liunan 0859519fea Merge pull request 'liunan' (#10) from liunan into master
Reviewed-on: #10
2024-09-13 14:43:34 +08:00
ln1778 58b2d52adf 33 2024-09-13 12:05:37 +08:00
ln1778 7a08110f8b 22 2024-09-13 12:05:17 +08:00
ln1778 146a02cdd9 333 2024-09-13 12:04:24 +08:00
ln1778 57ef27b43d 222 2024-09-13 11:23:27 +08:00
ln1778 8565ddb5a3 44 2024-09-13 11:21:38 +08:00
ln1778 578004cdb7 333 2024-09-13 11:15:27 +08:00
ln1778 829d0a2b63 22 2024-09-12 19:56:31 +08:00
ln1778 0d1f9fc845 22 2024-09-12 19:47:55 +08:00
ln1778 c99120819f 22 2024-09-12 19:27:09 +08:00
ln1778 c2278fb3cb 33 2024-09-12 19:26:51 +08:00
ln1778 bd2b674dc6 22 2024-09-12 19:23:13 +08:00
ln1778 1d6e67d6ed 22 2024-09-12 17:38:23 +08:00
ln1778 f91a8c0c7a 22 2024-09-12 17:10:28 +08:00
ln1778 3bbf8a51dc 22 2024-09-12 17:08:07 +08:00
ln1778 b631f64cbb 22 2024-09-12 16:57:41 +08:00
ln1778 083a5bf7d6 jjj 2024-09-12 16:45:08 +08:00
ln1778 1390f95d96 22 2024-09-12 16:44:45 +08:00
ln1778 0684ac1f09 22 2024-09-12 16:01:46 +08:00
ln1778 9675750d73 22 2024-09-12 14:18:32 +08:00
ln1778 29d5c8d26b 22 2024-09-12 14:18:17 +08:00
ln1778 d14d425a29 22 2024-09-12 13:45:10 +08:00
ln1778 756f3307f3 22 2024-09-12 09:53:40 +08:00
ln1778 ef3a38f1a7 22 2024-09-11 17:12:16 +08:00
ln1778 089e1a7c20 22 2024-09-11 17:11:35 +08:00
ln1778 007853d66e dd 2024-09-11 17:08:56 +08:00
ln1778 bf0006367b 22 2024-09-11 13:59:29 +08:00
ln1778 9e3d766d80 twt 2024-09-10 10:50:14 +08:00
ln1778 0bee153304 22 2024-09-10 10:47:01 +08:00
ln1778 baa8568ea8 ww 2024-09-10 10:37:32 +08:00
ln1778 e2c28f3bf2 da 2024-09-10 10:09:09 +08:00
ln1778 b74f0ca3fc jj 2024-09-09 15:24:33 +08:00
ln1778 2f9ddc22c0 dfdf 2024-09-09 15:13:37 +08:00
ln1778 703d815339 33 2024-09-09 14:47:36 +08:00
ln1778 5653581b8c 333 2024-09-09 14:27:02 +08:00
ln1778 11f09906cb 22 2024-09-06 18:16:54 +08:00
ln1778 96690a1063 Fdfd 2024-09-06 16:36:48 +08:00
ln1778 401608de32 22 2024-09-06 16:24:01 +08:00
ln1778 ba97fbca46 222 2024-09-06 16:17:51 +08:00
ln1778 fb6bba617a 2 2024-09-05 21:07:25 +08:00
ln1778 3f5e264a37 ee 2024-09-05 19:03:22 +08:00
ln1778 ab371f676a 22 2024-09-05 18:34:19 +08:00
ln1778 9675c4f2d5 df 2024-09-05 18:34:12 +08:00
ln1778 5fe305aa80 fdf 2024-09-05 18:31:19 +08:00
ln1778 b1b9b7ee1a 333 2024-09-05 17:32:53 +08:00
ln1778 b378f846a8 22 2024-09-03 10:34:56 +08:00
ln1778 9c3de1ec85 22 2024-09-03 10:34:41 +08:00
ln1778 7606e5c3eb 22 2024-09-03 10:34:06 +08:00
39 changed files with 47491 additions and 460 deletions

View File

@ -1,14 +1,15 @@
{ {
"name": "ag-element-ui", "name": "ag-element-ui",
"version": "0.1.0", "version": "0.1.20",
"private": true, "main": "packages/index.js",
"scripts": { "scripts": {
"dev": "vue-cli-service serve", "dev": "vue-cli-service serve",
"build": "vue-cli-service build", "build": "vue-cli-service build",
"lib": "vue-cli-service build --target lib --dest lib packages/index.js" "lib": "vue-cli-service build --target lib --dest lib packages/index.js"
}, },
"dependencies": { "dependencies": {
"core-js": "^3.8.3" "core-js": "^3.8.3",
"vuedraggable": "^2.24.3"
}, },
"devDependencies": { "devDependencies": {
"@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-babel": "~5.0.0",
@ -19,9 +20,9 @@
"element-ui": "^2.15.14", "element-ui": "^2.15.14",
"sass": "^1.32.7", "sass": "^1.32.7",
"sass-loader": "^12.0.0", "sass-loader": "^12.0.0",
"vue-template-compiler": "^2.6.14",
"vue": "^2.6.14", "vue": "^2.6.14",
"vue-router": "^3.5.1", "vue-router": "^3.5.1",
"vue-template-compiler": "^2.6.14",
"vuex": "^3.6.2" "vuex": "^3.6.2"
}, },
"browserslist": [ "browserslist": [

View File

@ -0,0 +1,9 @@
import agColorPicker from './src'
// 为组件提供 install 安装方法,供按需引入
agColorPicker.install = function (Vue) {
Vue.component(agColorPicker.name, agColorPicker)
}
// 导出组件
export default agColorPicker

View File

@ -0,0 +1,317 @@
const hsv2hsl = function(hue, sat, val) {
return [
hue,
(sat * val / ((hue = (2 - sat) * val) < 1 ? hue : 2 - hue)) || 0,
hue / 2
];
};
// Need to handle 1.0 as 100%, since once it is a number, there is no difference between it and 1
// <http://stackoverflow.com/questions/7422072/javascript-how-to-detect-number-as-a-decimal-including-1-0>
const isOnePointZero = function(n) {
return typeof n === 'string' && n.indexOf('.') !== -1 && parseFloat(n) === 1;
};
const isPercentage = function(n) {
return typeof n === 'string' && n.indexOf('%') !== -1;
};
// Take input from [0, n] and return it as [0, 1]
const bound01 = function(value, max) {
if (isOnePointZero(value)) value = '100%';
const processPercent = isPercentage(value);
value = Math.min(max, Math.max(0, parseFloat(value)));
// Automatically convert percentage into number
if (processPercent) {
value = parseInt(value * max, 10) / 100;
}
// Handle floating point rounding errors
if ((Math.abs(value - max) < 0.000001)) {
return 1;
}
// Convert into [0, 1] range if it isn't already
return (value % max) / parseFloat(max);
};
const INT_HEX_MAP = { 10: 'A', 11: 'B', 12: 'C', 13: 'D', 14: 'E', 15: 'F' };
const toHex = function({ r, g, b }) {
const hexOne = function(value) {
value = Math.min(Math.round(value), 255);
const high = Math.floor(value / 16);
const low = value % 16;
return '' + (INT_HEX_MAP[high] || high) + (INT_HEX_MAP[low] || low);
};
if (isNaN(r) || isNaN(g) || isNaN(b)) return '';
return '#' + hexOne(r) + hexOne(g) + hexOne(b);
};
const HEX_INT_MAP = { A: 10, B: 11, C: 12, D: 13, E: 14, F: 15 };
const parseHexChannel = function(hex) {
if (hex.length === 2) {
return (HEX_INT_MAP[hex[0].toUpperCase()] || +hex[0]) * 16 + (HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1]);
}
return HEX_INT_MAP[hex[1].toUpperCase()] || +hex[1];
};
const hsl2hsv = function(hue, sat, light) {
sat = sat / 100;
light = light / 100;
let smin = sat;
const lmin = Math.max(light, 0.01);
let sv;
let v;
light *= 2;
sat *= (light <= 1) ? light : 2 - light;
smin *= lmin <= 1 ? lmin : 2 - lmin;
v = (light + sat) / 2;
sv = light === 0 ? (2 * smin) / (lmin + smin) : (2 * sat) / (light + sat);
return {
h: hue,
s: sv * 100,
v: v * 100
};
};
// `rgbToHsv`
// Converts an RGB color value to HSV
// *Assumes:* r, g, and b are contained in the set [0, 255] or [0, 1]
// *Returns:* { h, s, v } in [0,1]
const rgb2hsv = function(r, g, b) {
r = bound01(r, 255);
g = bound01(g, 255);
b = bound01(b, 255);
const max = Math.max(r, g, b);
const min = Math.min(r, g, b);
let h, s;
let v = max;
const d = max - min;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0; // achromatic
} else {
switch (max) {
case r:
h = (g - b) / d + (g < b ? 6 : 0);
break;
case g:
h = (b - r) / d + 2;
break;
case b:
h = (r - g) / d + 4;
break;
}
h /= 6;
}
return { h: h * 360, s: s * 100, v: v * 100 };
};
// `hsvToRgb`
// Converts an HSV color value to RGB.
// *Assumes:* h is contained in [0, 1] or [0, 360] and s and v are contained in [0, 1] or [0, 100]
// *Returns:* { r, g, b } in the set [0, 255]
const hsv2rgb = function(h, s, v) {
h = bound01(h, 360) * 6;
s = bound01(s, 100);
v = bound01(v, 100);
const i = Math.floor(h);
const f = h - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
const mod = i % 6;
const r = [v, q, p, p, t, v][mod];
const g = [t, v, v, q, p, p][mod];
const b = [p, p, t, v, v, q][mod];
return {
r: Math.round(r * 255),
g: Math.round(g * 255),
b: Math.round(b * 255)
};
};
export default class Color {
constructor(options) {
this._hue = 0;
this._saturation = 100;
this._value = 100;
this._alpha = 100;
this.enableAlpha = false;
this.format = 'hex';
this.value = '';
options = options || {};
for (let option in options) {
if (options.hasOwnProperty(option)) {
this[option] = options[option];
}
}
this.doOnChange();
}
set(prop, value) {
if (arguments.length === 1 && typeof prop === 'object') {
for (let p in prop) {
if (prop.hasOwnProperty(p)) {
this.set(p, prop[p]);
}
}
return;
}
this['_' + prop] = value;
this.doOnChange();
}
get(prop) {
return this['_' + prop];
}
toRgb() {
return hsv2rgb(this._hue, this._saturation, this._value);
}
fromString(value) {
if (!value) {
this._hue = 0;
this._saturation = 100;
this._value = 100;
this.doOnChange();
return;
}
const fromHSV = (h, s, v) => {
this._hue = Math.max(0, Math.min(360, h));
this._saturation = Math.max(0, Math.min(100, s));
this._value = Math.max(0, Math.min(100, v));
this.doOnChange();
};
if (value.indexOf('hsl') !== -1) {
const parts = value.replace(/hsla|hsl|\(|\)/gm, '')
.split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
} else if (parts.length === 3) {
this._alpha = 100;
}
if (parts.length >= 3) {
const { h, s, v } = hsl2hsv(parts[0], parts[1], parts[2]);
fromHSV(h, s, v);
}
} else if (value.indexOf('hsv') !== -1) {
const parts = value.replace(/hsva|hsv|\(|\)/gm, '')
.split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
} else if (parts.length === 3) {
this._alpha = 100;
}
if (parts.length >= 3) {
fromHSV(parts[0], parts[1], parts[2]);
}
} else if (value.indexOf('rgb') !== -1) {
const parts = value.replace(/rgba|rgb|\(|\)/gm, '')
.split(/\s|,/g).filter((val) => val !== '').map((val, index) => index > 2 ? parseFloat(val) : parseInt(val, 10));
if (parts.length === 4) {
this._alpha = Math.floor(parseFloat(parts[3]) * 100);
} else if (parts.length === 3) {
this._alpha = 100;
}
if (parts.length >= 3) {
const { h, s, v } = rgb2hsv(parts[0], parts[1], parts[2]);
fromHSV(h, s, v);
}
} else if (value.indexOf('#') !== -1) {
const hex = value.replace('#', '').trim();
if (!/^(?:[0-9a-fA-F]{3}){1,2}|[0-9a-fA-F]{8}$/.test(hex)) return;
let r, g, b;
if (hex.length === 3) {
r = parseHexChannel(hex[0] + hex[0]);
g = parseHexChannel(hex[1] + hex[1]);
b = parseHexChannel(hex[2] + hex[2]);
} else if (hex.length === 6 || hex.length === 8) {
r = parseHexChannel(hex.substring(0, 2));
g = parseHexChannel(hex.substring(2, 4));
b = parseHexChannel(hex.substring(4, 6));
}
if (hex.length === 8) {
this._alpha = Math.floor(parseHexChannel(hex.substring(6)) / 255 * 100);
} else if (hex.length === 3 || hex.length === 6) {
this._alpha = 100;
}
const { h, s, v } = rgb2hsv(r, g, b);
fromHSV(h, s, v);
}
}
compare(color) {
return Math.abs(color._hue - this._hue) < 2 &&
Math.abs(color._saturation - this._saturation) < 1 &&
Math.abs(color._value - this._value) < 1 &&
Math.abs(color._alpha - this._alpha) < 1;
}
doOnChange() {
const { _hue, _saturation, _value, _alpha, format } = this;
if (this.enableAlpha) {
switch (format) {
case 'hsl':
const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
this.value = `hsla(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%, ${ _alpha / 100})`;
break;
case 'hsv':
this.value = `hsva(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%, ${ _alpha / 100})`;
break;
default:
const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
this.value = `rgba(${r}, ${g}, ${b}, ${ _alpha / 100 })`;
}
} else {
switch (format) {
case 'hsl':
const hsl = hsv2hsl(_hue, _saturation / 100, _value / 100);
this.value = `hsl(${ _hue }, ${ Math.round(hsl[1] * 100) }%, ${ Math.round(hsl[2] * 100) }%)`;
break;
case 'hsv':
this.value = `hsv(${ _hue }, ${ Math.round(_saturation) }%, ${ Math.round(_value) }%)`;
break;
case 'rgb':
const { r, g, b } = hsv2rgb(_hue, _saturation, _value);
this.value = `rgb(${r}, ${g}, ${b})`;
break;
default:
this.value = toHex(hsv2rgb(_hue, _saturation, _value));
}
}
}
};

View File

@ -0,0 +1,132 @@
<template>
<div class="el-color-alpha-slider" :class="{ 'is-vertical': vertical }">
<div class="el-color-alpha-slider__bar"
@click="handleClick"
ref="bar"
:style="{
background: background
}">
</div>
<div class="el-color-alpha-slider__thumb"
ref="thumb"
:style="{
left: thumbLeft + 'px',
top: thumbTop + 'px'
}">
</div>
</div>
</template>
<script>
import draggable from '../draggable';
export default {
name: 'el-color-alpha-slider',
props: {
color: {
required: true
},
vertical: Boolean
},
watch: {
'color._alpha'() {
this.update();
},
'color.value'() {
this.update();
}
},
methods: {
handleClick(event) {
const thumb = this.$refs.thumb;
const target = event.target;
if (target !== thumb) {
this.handleDrag(event);
}
},
handleDrag(event) {
const rect = this.$el.getBoundingClientRect();
const { thumb } = this.$refs;
if (!this.vertical) {
let left = event.clientX - rect.left;
left = Math.max(thumb.offsetWidth / 2, left);
left = Math.min(left, rect.width - thumb.offsetWidth / 2);
this.color.set('alpha', Math.round((left - thumb.offsetWidth / 2) / (rect.width - thumb.offsetWidth) * 100));
} else {
let top = event.clientY - rect.top;
top = Math.max(thumb.offsetHeight / 2, top);
top = Math.min(top, rect.height - thumb.offsetHeight / 2);
this.color.set('alpha', Math.round((top - thumb.offsetHeight / 2) / (rect.height - thumb.offsetHeight) * 100));
}
},
getThumbLeft() {
if (this.vertical) return 0;
const el = this.$el;
const alpha = this.color._alpha;
if (!el) return 0;
const thumb = this.$refs.thumb;
return Math.round(alpha * (el.offsetWidth - thumb.offsetWidth / 2) / 100);
},
getThumbTop() {
if (!this.vertical) return 0;
const el = this.$el;
const alpha = this.color._alpha;
if (!el) return 0;
const thumb = this.$refs.thumb;
return Math.round(alpha * (el.offsetHeight - thumb.offsetHeight / 2) / 100);
},
getBackground() {
if (this.color && this.color.value) {
const { r, g, b } = this.color.toRgb();
return `linear-gradient(to right, rgba(${r}, ${g}, ${b}, 0) 0%, rgba(${r}, ${g}, ${b}, 1) 100%)`;
}
return null;
},
update() {
this.thumbLeft = this.getThumbLeft();
this.thumbTop = this.getThumbTop();
this.background = this.getBackground();
}
},
data() {
return {
thumbLeft: 0,
thumbTop: 0,
background: null
};
},
mounted() {
const { bar, thumb } = this.$refs;
const dragConfig = {
drag: (event) => {
this.handleDrag(event);
},
end: (event) => {
this.handleDrag(event);
}
};
draggable(bar, dragConfig);
draggable(thumb, dragConfig);
this.update();
}
};
</script>

View File

@ -0,0 +1,123 @@
<template>
<div class="el-color-hue-slider" :class="{ 'is-vertical': vertical }">
<div class="el-color-hue-slider__bar" @click="handleClick" ref="bar"></div>
<div class="el-color-hue-slider__thumb"
:style="{
left: thumbLeft + 'px',
top: thumbTop + 'px'
}"
ref="thumb">
</div>
</div>
</template>
<script>
import draggable from '../draggable';
export default {
name: 'el-color-hue-slider',
props: {
color: {
required: true
},
vertical: Boolean
},
data() {
return {
thumbLeft: 0,
thumbTop: 0
};
},
computed: {
hueValue() {
const hue = this.color.get('hue');
return hue;
}
},
watch: {
hueValue() {
this.update();
}
},
methods: {
handleClick(event) {
const thumb = this.$refs.thumb;
const target = event.target;
if (target !== thumb) {
this.handleDrag(event);
}
},
handleDrag(event) {
const rect = this.$el.getBoundingClientRect();
const { thumb } = this.$refs;
let hue;
if (!this.vertical) {
let left = event.clientX - rect.left;
left = Math.min(left, rect.width - thumb.offsetWidth / 2);
left = Math.max(thumb.offsetWidth / 2, left);
hue = Math.round((left - thumb.offsetWidth / 2) / (rect.width - thumb.offsetWidth) * 360);
} else {
let top = event.clientY - rect.top;
top = Math.min(top, rect.height - thumb.offsetHeight / 2);
top = Math.max(thumb.offsetHeight / 2, top);
hue = Math.round((top - thumb.offsetHeight / 2) / (rect.height - thumb.offsetHeight) * 360);
}
this.color.set('hue', hue);
},
getThumbLeft() {
if (this.vertical) return 0;
const el = this.$el;
const hue = this.color.get('hue');
if (!el) return 0;
const thumb = this.$refs.thumb;
return Math.round(hue * (el.offsetWidth - thumb.offsetWidth / 2) / 360);
},
getThumbTop() {
if (!this.vertical) return 0;
const el = this.$el;
const hue = this.color.get('hue');
if (!el) return 0;
const thumb = this.$refs.thumb;
return Math.round(hue * (el.offsetHeight - thumb.offsetHeight / 2) / 360);
},
update() {
this.thumbLeft = this.getThumbLeft();
this.thumbTop = this.getThumbTop();
}
},
mounted() {
const { bar, thumb } = this.$refs;
const dragConfig = {
drag: (event) => {
this.handleDrag(event);
},
end: (event) => {
this.handleDrag(event);
}
};
draggable(bar, dragConfig);
draggable(thumb, dragConfig);
this.update();
}
};
</script>

View File

@ -0,0 +1,128 @@
<template>
<transition name="el-zoom-in-top" @after-leave="doDestroy">
<div
class="el-color-dropdown"
v-show="showPopper">
<div class="el-color-dropdown__main-wrapper">
<hue-slider ref="hue" :color="color" vertical style="float: right;"></hue-slider>
<sv-panel ref="sl" :color="color"></sv-panel>
</div>
<alpha-slider v-if="showAlpha" ref="alpha" :color="color"></alpha-slider>
<predefine v-if="predefine" :color="color" :colors="predefine"></predefine>
<div class="el-color-dropdown__btns">
<span class="el-color-dropdown__value">
<el-input
v-model="customInput"
@keyup.native.enter="handleConfirm"
@blur="handleConfirm"
:validate-event="false"
size="mini">
</el-input>
</span>
<el-button
size="mini"
type="text"
class="el-color-dropdown__link-btn"
@click="$emit('eyedropper')">
<i class="el-icon-magic-stick"></i>
</el-button>
<el-button
size="mini"
type="text"
class="el-color-dropdown__link-btn"
@click="$emit('clear')">
{{ t('el.colorpicker.clear') }}
</el-button>
<el-button
plain
size="mini"
class="el-color-dropdown__btn"
@click="confirmValue">
{{ t('el.colorpicker.confirm') }}
</el-button>
</div>
</div>
</transition>
</template>
<script>
import SvPanel from './sv-panel';
import HueSlider from './hue-slider';
import AlphaSlider from './alpha-slider';
import Predefine from './predefine';
import Popper from 'element-ui/src/utils/vue-popper';
import Locale from 'element-ui/src/mixins/locale';
import ElInput from 'element-ui/packages/input';
import ElButton from 'element-ui/packages/button';
export default {
name: 'el-color-picker-dropdown',
mixins: [Popper, Locale],
components: {
SvPanel,
HueSlider,
AlphaSlider,
ElInput,
ElButton,
Predefine
},
props: {
color: {
required: true
},
showAlpha: Boolean,
predefine: Array
},
data() {
return {
customInput: ''
};
},
computed: {
currentColor() {
const parent = this.$parent;
return !parent.value && !parent.showPanelColor ? '' : parent.color.value;
}
},
methods: {
confirmValue() {
this.$emit('pick');
},
handleConfirm() {
this.color.fromString(this.customInput);
}
},
mounted() {
this.$parent.popperElm = this.popperElm = this.$el;
this.referenceElm = this.$parent.$el;
},
watch: {
showPopper(val) {
if (val === true) {
this.$nextTick(() => {
const { sl, hue, alpha } = this.$refs;
sl && sl.update();
hue && hue.update();
alpha && alpha.update();
});
}
},
currentColor: {
immediate: true,
handler(val) {
this.customInput = val;
}
}
}
};
</script>

View File

@ -0,0 +1,61 @@
<template>
<div class="el-color-predefine">
<div class="el-color-predefine__colors">
<div class="el-color-predefine__color-selector"
:class="{selected: item.selected, 'is-alpha': item._alpha < 100}"
v-for="(item, index) in rgbaColors"
:key="colors[index]"
@click="handleSelect(index)">
<div :style="{'background-color': item.value}">
</div>
</div>
</div>
</div>
</template>
<script>
import Color from '../color';
export default {
props: {
colors: { type: Array, required: true },
color: { required: true }
},
data() {
return {
rgbaColors: this.parseColors(this.colors, this.color)
};
},
methods: {
handleSelect(index) {
this.color.fromString(this.colors[index]);
},
parseColors(colors, color) {
return colors.map(value => {
const c = new Color();
c.enableAlpha = true;
c.format = 'rgba';
c.fromString(value);
c.selected = c.value === color.value;
return c;
});
}
},
watch: {
'$parent.currentColor'(val) {
const color = new Color();
color.fromString(val);
this.rgbaColors.forEach(item => {
item.selected = color.compare(item);
});
},
colors(newVal) {
this.rgbaColors = this.parseColors(newVal, this.color);
},
color(newVal) {
this.rgbaColors = this.parseColors(this.colors, newVal);
}
}
};
</script>

View File

@ -0,0 +1,100 @@
<template>
<div class="el-color-svpanel"
:style="{
backgroundColor: background
}">
<div class="el-color-svpanel__white"></div>
<div class="el-color-svpanel__black"></div>
<div class="el-color-svpanel__cursor"
:style="{
top: cursorTop + 'px',
left: cursorLeft + 'px'
}">
<div></div>
</div>
</div>
</template>
<script>
import draggable from '../draggable';
export default {
name: 'el-sl-panel',
props: {
color: {
required: true
}
},
computed: {
colorValue() {
const hue = this.color.get('hue');
const value = this.color.get('value');
return { hue, value };
}
},
watch: {
colorValue() {
this.update();
}
},
methods: {
update() {
const saturation = this.color.get('saturation');
const value = this.color.get('value');
const el = this.$el;
let { clientWidth: width, clientHeight: height } = el;
this.cursorLeft = saturation * width / 100;
this.cursorTop = (100 - value) * height / 100;
this.background = 'hsl(' + this.color.get('hue') + ', 100%, 50%)';
},
handleDrag(event) {
const el = this.$el;
const rect = el.getBoundingClientRect();
let left = event.clientX - rect.left;
let top = event.clientY - rect.top;
left = Math.max(0, left);
left = Math.min(left, rect.width);
top = Math.max(0, top);
top = Math.min(top, rect.height);
this.cursorLeft = left;
this.cursorTop = top;
this.color.set({
saturation: left / rect.width * 100,
value: 100 - top / rect.height * 100
});
}
},
mounted() {
draggable(this.$el, {
drag: (event) => {
this.handleDrag(event);
},
end: (event) => {
this.handleDrag(event);
}
});
this.update();
},
data() {
return {
cursorTop: 0,
cursorLeft: 0,
background: 'hsl(0, 100%, 50%)'
};
}
};
</script>

View File

@ -0,0 +1,36 @@
import Vue from 'vue';
let isDragging = false;
export default function(element, options) {
if (Vue.prototype.$isServer) return;
const moveFn = function(event) {
if (options.drag) {
options.drag(event);
}
};
const upFn = function(event) {
document.removeEventListener('mousemove', moveFn);
document.removeEventListener('mouseup', upFn);
document.onselectstart = null;
document.ondragstart = null;
isDragging = false;
if (options.end) {
options.end(event);
}
};
element.addEventListener('mousedown', function(event) {
if (isDragging) return;
document.onselectstart = function() { return false; };
document.ondragstart = function() { return false; };
document.addEventListener('mousemove', moveFn);
document.addEventListener('mouseup', upFn);
isDragging = true;
if (options.start) {
options.start(event);
}
});
}

View File

@ -0,0 +1,201 @@
<template>
<div
:class="[
'el-color-picker',
colorDisabled ? 'is-disabled' : '',
colorSize ? `el-color-picker--${ colorSize }` : ''
]"
v-clickoutside="hide">
<div class="el-color-picker__mask" v-if="colorDisabled"></div>
<div class="el-color-picker__trigger" @click="handleTrigger">
<span class="el-color-picker__color" :class="{ 'is-alpha': showAlpha }">
<span class="el-color-picker__color-inner"
:style="{
backgroundColor: displayedColor
}"></span>
<span class="el-color-picker__empty el-icon-close" v-if="!value && !showPanelColor"></span>
</span>
<span class="el-color-picker__icon el-icon-arrow-down" v-show="value || showPanelColor"></span>
</div>
<picker-dropdown
ref="dropdown"
:class="['el-color-picker__panel', popperClass || '']"
v-model="showPicker"
@pick="confirmValue"
@clear="clearValue"
@eyedropper="eyedropper"
:color="color"
:show-alpha="showAlpha"
:predefine="predefine">
</picker-dropdown>
</div>
</template>
<script>
import Color from './color';
import PickerDropdown from './components/picker-dropdown.vue';
import Clickoutside from 'element-ui/src/utils/clickoutside';
import Emitter from 'element-ui/src/mixins/emitter';
export default {
name: 'AgColorPicker',
mixins: [Emitter],
props: {
value: String,
showAlpha: Boolean,
colorFormat: String,
disabled: Boolean,
size: String,
popperClass: String,
predefine: Array
},
inject: {
elForm: {
default: ''
},
elFormItem: {
default: ''
}
},
directives: { Clickoutside },
computed: {
displayedColor() {
if (!this.value && !this.showPanelColor) {
return 'transparent';
}
return this.displayedRgb(this.color, this.showAlpha);
},
_elFormItemSize() {
return (this.elFormItem || {}).elFormItemSize;
},
colorSize() {
return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;
},
colorDisabled() {
return this.disabled || (this.elForm || {}).disabled;
}
},
watch: {
value(val) {
if (!val) {
this.showPanelColor = false;
} else if (val && val !== this.color.value) {
this.color.fromString(val);
}
},
color: {
deep: true,
handler() {
this.showPanelColor = true;
}
},
displayedColor(val) {
if (!this.showPicker) return;
const currentValueColor = new Color({
enableAlpha: this.showAlpha,
format: this.colorFormat
});
currentValueColor.fromString(this.value);
const currentValueColorRgb = this.displayedRgb(currentValueColor, this.showAlpha);
if (val !== currentValueColorRgb) {
this.$emit('active-change', val);
}
}
},
methods: {
handleTrigger() {
if (this.colorDisabled) return;
this.showPicker = !this.showPicker;
},
confirmValue() {
const value = this.color.value;
this.$emit('input', value);
this.$emit('change', value);
this.dispatch('ElFormItem', 'el.form.change', value);
this.showPicker = false;
},
clearValue() {
this.$emit('input', null);
this.$emit('change', null);
if (this.value !== null) {
this.dispatch('ElFormItem', 'el.form.change', null);
}
this.showPanelColor = false;
this.showPicker = false;
this.resetColor();
},
async eyedropper(){
const dropper=new EyeDropper();
try{
const result=await dropper.open();
const value = result.sRGBHex;
this.$emit('input', value);
this.$emit('change', value);
this.dispatch('ElFormItem', 'el.form.change', value);
}catch(e){
}
},
hide() {
this.showPicker = false;
this.resetColor();
},
resetColor() {
this.$nextTick(_ => {
if (this.value) {
this.color.fromString(this.value);
} else {
this.showPanelColor = false;
}
});
},
displayedRgb(color, showAlpha) {
if (!(color instanceof Color)) {
throw Error('color should be instance of Color Class');
}
const { r, g, b } = color.toRgb();
return showAlpha
? `rgba(${ r }, ${ g }, ${ b }, ${ color.get('alpha') / 100 })`
: `rgb(${ r }, ${ g }, ${ b })`;
}
},
mounted() {
const value = this.value;
if (value) {
this.color.fromString(value);
}
this.popperElm = this.$refs.dropdown.$el;
},
data() {
const color = new Color({
enableAlpha: this.showAlpha,
format: this.colorFormat
});
return {
color,
showPicker: false,
showPanelColor: false
};
},
components: {
PickerDropdown
}
};
</script>

View File

@ -6,4 +6,4 @@ agDatePicker.install = function (Vue) {
} }
// 导出组件 // 导出组件
export default agDatePicker export default agDatePicker;

View File

@ -3,9 +3,12 @@
:style="{ width }" :style="{ width }"
:prefix-icon="iconClass" :prefix-icon="iconClass"
clear-icon="ag-icon-clear" clear-icon="ag-icon-clear"
ref="datepickerref"
v-model="dateArr" v-model="dateArr"
v-bind="attrs" v-bind="attrs"
v-on="Listeners" v-on="Listeners"
:size="$attrs.size||'small'"
:value="value"
:type="datetype" :type="datetype"
@mouseenter.native="mousetrue = true" @mouseenter.native="mousetrue = true"
@mouseleave.native="mousetrue = false" @mouseleave.native="mousetrue = false"
@ -13,13 +16,10 @@
</template> </template>
<script> <script>
/**
* agDatePicker 时间选择器
*/
import isEmpty from "../../../src/utils/isEmpty"; import isEmpty from "../../../src/utils/isEmpty";
export default { export default {
name: "agDatePicker", name: "AgDatePicker",
props: { props: {
range:false, range:false,
showTime:false, showTime:false,
@ -28,7 +28,9 @@ export default {
default: "date", default: "date",
}, },
value: { value: {
default: null||[], default:()=>{
return null||[]
},
}, },
}, },
data() { data() {
@ -59,9 +61,9 @@ export default {
let config = {}; let config = {};
if (this.range) { if (this.range) {
config = { config = {
"start-placeholder": "开始日期", "start-placeholder":this.$attrs.startPlaceholder||"开始日期",
"end-placeholder": "结束日期", "end-placeholder":this.$attrs.endPlaceholder|| "结束日期",
"range-separator": "-", "range-separator":this.$attrs.rangeSeparator||"-",
"picker-options": { "picker-options": {
disabledDate(time) { disabledDate(time) {
return ( return (
@ -130,7 +132,7 @@ export default {
} else { } else {
config = { config = {
align: "right", align: "right",
placeholder: "选择日期", placeholder: this.$attrs.placeholder||"选择日期",
"picker-options": { "picker-options": {
disabledDate(time) { disabledDate(time) {
return time.getTime() > Date.now(); return time.getTime() > Date.now();
@ -179,33 +181,55 @@ export default {
input: (value) => { input: (value) => {
if (this.range) { if (this.range) {
if (!isEmpty(value) && value.length === 2 && value[0] && value[1]) { if (!isEmpty(value) && value.length === 2 && value[0] && value[1]) {
if(this.showTime){
if(this.$listeners.change){
this.$emit("change", value);
}
}else{
if(this.$listeners.change){
this.$emit("change", [ this.$emit("change", [
`${value[0]} 00:00:00`, `${value[0]} 00:00:00`,
`${value[1]} 23:59:59`, `${value[1]} 23:59:59`,
]); ]);
}
}
} else { } else {
if(this.$listeners.change){
this.$emit("change", []); this.$emit("change", []);
} }
}
} else { } else {
if(this.$listeners.change){
this.$emit("change", value); this.$emit("change", value);
} }
}
}, },
}); });
}, },
iconClass() { iconClass() {
return this.mousetrue return this.mousetrue
? "ag-icon-prefix-hide" ? "ag-icon-prefix-hide"
: "ag-icon-prefix-show"; : "ag-icon-prefix-show";
}, },
}, },
mounted() {
if (Array.isArray(this.value)&&this.range){
this.dateArr = [this.value[0] || "", this.value[1] || ""];
} else {
this.dateArr = this.value;
}
for(const key in this.$refs.datepickerref){
if(!this[key]&&key!='value'){
this[key]=this.$refs.datepickerref[key];
}
}
},
watch: { watch: {
value: { value: {
handler(newVal) { handler(newVal) {
if (Array.isArray(newVal)&&this.range){ if (Array.isArray(newVal)&&this.range){
this.dateArr = [newVal[0] || "", newVal[1] || ""];
const [date1, date2] = newVal.slice(0, 2);
this.dateArr = [date1 || "", date2 || ""];
} else { } else {
this.dateArr = newVal; this.dateArr = newVal;
} }
@ -217,7 +241,7 @@ export default {
if(!newVal){ if(!newVal){
return return
} }
let defaultWidth = this.range ?this.showTime?"330px":"170px" : this.showTime?"190px":"140px"; let defaultWidth = this.range ?this.showTime?"330px":"200px" : this.showTime?"190px":"150px";
this.width = defaultWidth; this.width = defaultWidth;
}, },
immediate: true, immediate: true,

9
packages/agForm/index.js Normal file
View File

@ -0,0 +1,9 @@
import agForm from './src'
// 为组件提供 install 安装方法,供按需引入
agForm.install = function (Vue) {
Vue.component(agForm.name, agForm)
}
// 导出组件
export default agForm;

View File

@ -0,0 +1,191 @@
<template>
<el-form v-bind="$attrs" v-on="$listeners" :model="formValue" ref="formref">
<ag-row>
<ag-col v-for="item,index in data" :key="index" :span="item.span||24">
<el-form-item v-bind="item" :prop="item.enName" >
<slot :name="item.soltName" v-if="item.soltName" :row="item"/>
<div class="contentbox" v-else>
<ag-input v-model="formValue[item.enName]" type="textarea" v-bind="item" v-if="item.type=='ag-textarea'" size="small" clearable></ag-input>
<ag-input v-model="formValue[item.enName]" v-bind="item" v-if="item.type=='ag-input'" size="small" clearable></ag-input>
<ag-date-picker v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-date-picker'" size="small" clearable/>
<ag-select v-model="formValue[item.enName]" v-bind="item" v-if="item.type=='ag-select'" size="small" clearable/>
<el-radio-group v-model="formValue[item.enName]" v-bind="item" v-if="item.type=='ag-radio'&&formValue[item.enName]" size="small" clearable>
<el-radio :label="it.value" v-for="it,index in item.options" :key="index">{{it.label}}</el-radio>
</el-radio-group>
<el-checkbox-group v-bind="item" v-if="item.type=='ag-checkbox'&&formValue[item.enName]" v-model="formValue[item.enName]" size="small" clearable>
<el-checkbox v-for="it,index in item.options" :label="it.value" :key="index">{{it.label}}</el-checkbox>
</el-checkbox-group>
<el-input-number v-model="formValue[item.enName]" v-bind="item" v-if="item.type=='ag-input-number'" clearable></el-input-number>
<el-cascader v-model="formValue[item.enName]" v-bind="item" size="small" v-if="item.type=='ag-cascader'" clearable :style="{width:'100%'}"></el-cascader>
<el-switch
v-model="formValue[item.enName]"
v-bind="item" size="small" v-if="item.type=='ag-switch'&&formValue[item.enName]" >
</el-switch>
<el-slider v-model="formValue[item.enName]"
v-bind="item" size="small" v-if="item.type=='ag-slider'"></el-slider>
<el-time-select
:style="{width:'100%'}"
clearable
v-model="formValue[item.enName]"
v-bind="item" size="small"
v-if="item.type=='ag-time-select'"
:picker-options="item.options">
</el-time-select>
<ag-upload v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-upload'" clear isPre></ag-upload>
<el-rate v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-rate'" size="small"></el-rate>
<agColorPicker v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-color-picker'" size="small">
</agColorPicker>
<el-transfer :data="item.data" v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-transfer'" size="small"></el-transfer>
<agNumberRange v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-numberRange'" size="small" clearable/>
<ag-multion-input v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-multion-input'" size="small" clearable/>
<agQuery v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-query'" size="small" clearable/>
<ag-mution-checkbox :datas="item.datas" v-bind="item" :placeholder="item.placeholder" v-model="formValue[item.enName]" v-if="item.type=='ag-mution-checkbox'" size="small"> </ag-mution-checkbox>
<ag-multion-date-picker v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-multion-datte-picker'" size="small"/>
</div>
</el-form-item>
</ag-col>
</ag-row>
<div class="footerbox" v-if="$listeners.onSubmit">
<slot name="button" :value="formValue">
<el-button @click="onSubmit" type="primary" size="small" class="submitbtn">
{{submittitle||'提交'}}
</el-button>
</slot>
</div>
</el-form>
</template>
<script>
import agInput from './../../agInput/src/index.vue';
import agDatePicker from './../../agDatePicker/src/index.vue';
import agSelect from './../../agSelect/src/index.vue';
import agRow from './../../agRow/src/index.vue';
import agCol from './../../agCol/src/index.vue';
import agUpload from './../../agUpload/src/index.vue';
import agColorPicker from './../../agColorPicker/src/index.vue';
import agNumberRange from './../../agNumberRange/src/index.vue';
import agMultionInput from './../../agMultionInput/src/index.vue';
import agQuery from './../../agQuery/src/index.vue';
import agMultionDatePicker from '../../agMultionDatePicker';
export default{
name:"agForm",
components:{
agMultionDatePicker,
agInput:agInput,
agDatePicker,
agSelect,
agRow,
agCol,
agUpload:agUpload,
agColorPicker,
agNumberRange,
agMultionInput,
agQuery
},
props:{
submittitle:{
type:String,
default:'提交'
},
data:{
type:Array,
default(){
return []
}
},
span:{
type:Number,
default:24
},
},
data(){
return{
formValue:{
}
}
},
mounted(){
this.formValue=this.$attrs.value;
setTimeout(()=>{
this.$refs.formref.clearValidate();
},1);
if(this.$refs.formref){
for(const key in this.$refs.formref){
if(!this[key]&&key!='value'){
this[key]=this.$refs.formref[key];
}
}
}
},
watch:{
value(val){
this.formValue = val;
setTimeout(()=>{
this.$refs.formref.clearValidate();
},1);
}
},
methods:{
onSubmit(){
this.$emit('onSubmit',this.formValue)
}
},
}
</script>
<style lang="scss" scoped>
.footerbox{
margin-top:12px;
display:flex;
justify-content:center;
.submitbtn{
width:200px;
}
}
.el-form--inline{
.el-form-item{
display:flex;
}
.el-form-item__content{
flex:1;
width:100%;
}
}
.contentbox{
width:100%;
display: flex;
align-items: stretch;
}
.flexcenter{
display: flex;
align-items: stretch;
}
::v-deep{
.el-form--inline{
.el-form-item{
display:flex;
}
.el-form-item__content{
display:flex;
flex:1;
width:100%;
margin-left:0;
}
}
.el-form-item{
width:100%;
margin-bottom: 12px;
}
.el-form-item__content{
flex:1;
display:flex;
width:100%;
margin-left:0!important;
}
}
</style>

View File

@ -1,23 +1,17 @@
<template> <template>
<el-input <el-input
class="ag_input" class="ag_input"
size="small" :size="$attrs.size||'small'"
ref="apinputref"
:style="{ width }" :style="{ width }"
v-on="Listeners" v-bind="$attrs"
v-bind="attrs" v-on="$listeners"
> >
<slot name="append" slot="append" /> <slot v-for="(_,name) in $slots" :name="name" :slot="name"> </slot>
<slot name="prefix" slot="prefix" />
<slot name="suffix" slot="suffix" />
<slot name="prepend" slot="prepend" />
</el-input> </el-input>
</template> </template>
<script> <script>
/**
* WInput
* @desc 处理输入的输入框(转大写不能有中文空格等)
*/
export default { export default {
name: "agInput", name: "agInput",
props: { props: {
@ -30,42 +24,15 @@ export default {
default: false, default: false,
}, },
}, },
computed: { mounted(){
attrs() { if(this.$refs.apinputref){
return { for(const key in this.$refs.apinputref){
size: "small", if(!this[key]&&key!='value'){
clearable: true, // this[key]=this.$refs.apinputref[key];
...this.$attrs, }
}; }
},
//
Listeners() {
return Object.assign(
{},
//
this.$listeners,
//
//
{
// `v-model`
input: (value) => {
this.$emit("change", this.toUpperCase ? value.toUpperCase() : value);
},
blur: (e) => {
let value = e.target.value
.trim()
.replace(/\s/g, (match) =>
match.charCodeAt(0) === 12288 ? String.fromCharCode(32) : match
);
//
this.$emit("change", value);
},
} }
);
}, },
},
mounted() {},
}; };
</script> </script>

View File

@ -1,9 +0,0 @@
import agMultifunctionSearch from './src'
// 为组件提供 install 安装方法,供按需引入
agMultifunctionSearch.install = function (Vue) {
Vue.component(agMultifunctionSearch.name, agMultifunctionSearch)
}
// 导出组件
export default agMultifunctionSearch

View File

@ -1,252 +0,0 @@
<template>
<div class="ag-MultifunctionSearch">
<ag-select
slot="prepend"
:clearable="false"
placeholder="请选择"
:value="value[0]"
v-bind="config.select"
@change="onSelect"
class="ag_select_group"
:style="{ width: `${swidth}px` }"
>
</ag-select>
<ag-input
v-if="!showExtra"
class="ag_input_group"
placeholder="请输入内容"
:value="value[1]"
:disabled="config.input.disabled||imeipopover"
v-bind="config.input"
@change="onInputChange"
>
</ag-input>
<el-popover
v-if="showExtra"
placement="bottom"
trigger="manual"
ref="popover"
popper-class="popperOptions"
v-model="imeipopover"
>
<template slot="reference">
<ag-input
class="ag_input_group"
placeholder="请输入内容"
:value="value[1]"
:disabled="config.input.disabled||imeipopover"
v-bind="config.input"
@change="onInputChange"
>
<template slot="suffix" v-if="showExtra">
<i
class="iconfont icon-icon-piliangcaozuo imeiicondefault"
:class="imeipopover && 'imeiiconActive'"
style="font-size: 12px;"
@click.prevent="imeiiconClick"
></i>
</template>
</ag-input>
</template>
<el-input
v-model="value[1]"
type="textarea"
:rows="12"
placeholder="一行一项最多支持200行"
resize="none"
border="none"
class="textareainput"
@input="onInputChange"
/>
<divider class="divider"></divider>
<div class="btnbox">
<div>
<el-button size="mini" @click="imeiclose"
>关闭</el-button
>
</div>
<div>
<el-button size="mini" @click="cleartext"
>清空</el-button
>
<el-button
size="mini"
type="primary"
plain
@click="search"
>查询</el-button
>
</div>
</div>
</el-popover>
</div>
</template>
<script>
import agSelect from "../../agSelect/src/index.vue";
import agInput from "../../agInput/src/index.vue";
import {Divider} from "element-ui";
export default {
name: "agMultifunctionSearch",
components: {
agSelect,
agInput,
Divider
},
props: {
value: {
type: Array,
default: () => {
return [null, null];
},
},
options: {
type: Array,
default: () => {
return [];
},
},
showExtra: {
type: Boolean,
default: false,
},
},
data() {
return {
values: [null, null],
swidth:90,
selectvalue:"",
imeipopover:false
};
},
computed: {
config() {
const input = {
...this.$attrs
};
const select = {
...this.$attrs,
options:this.options
};
return {
input,
select,
};
},
},
watch: {
value: {
handler(newVal) {
if (!Array.isArray(newVal)) {
throw new Error("请传入数组");
}
let newselectValue=newVal[0];
const find=this.options.find((f)=>f.value==newselectValue);
if(find){
let fontwidth=this.getStringWidth(find?.label);
this.swidth=fontwidth>165?165:fontwidth;
}
},
immediate: true,
},
},
methods: {
onSelect(value){
this.$emit("change", [value, this.value[1]||""]);
},
onInputChange(value){
this.$emit("change", [ this.value[0] ||"",value]);
},
getStringWidth(text) {
let font = "13px";
//
let element = document.createElement('span');
element.style.visibility = 'hidden';
element.style.whiteSpace = 'nowrap';
element.style.font = font; //
element.textContent = text;
//
document.body.appendChild(element);
//
let width = element.offsetWidth;
//
document.body.removeChild(element);
return width+50;
},
imeiiconClick(){
this.imeipopover=!this.imeipopover;
},
imeiclose(){
this.imeipopover=false;
},
cleartext(){
this.value[1]="";
this.$set(this.value,1,"");
},
search(){
this.imeipopover=false;
this.$emit("onSrarch", this.value);
},
getAfterAgo(attr, prefix) {
const config = {};
for (const key in attr) {
if (attr.hasOwnProperty(key) && key.startsWith(prefix)) {
const newKey = key.substring(prefix.length);
config[newKey] = attr[key];
}
}
return config;
},
},
};
</script>
<style lang="scss">
.popperOptions {
min-width: 200px !important;
padding: 0px !important;
}
</style>
<style scoped lang='scss'>
.ag-MultifunctionSearch {
display: flex;
align-items: center;
}
.imeiicondefault {
padding: 5px;
border-radius: 4px;
background: #f0f2f5;
cursor: pointer;
}
.imeiiconActive {
cursor: pointer;
color: #409eff;
background: #e9f1fc;
}
.divider {
margin: 0;
}
.btnbox {
padding: 5px;
display: flex;
justify-content: space-between;
}
::v-deep{
.el-textarea__inner{
border:none;
}
.el-input__suffix-inner{
height:100%;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

View File

@ -0,0 +1,9 @@
import agMultionDatePicker from './src';
// 为组件提供 install 安装方法,供按需引入
agMultionDatePicker.install = function (Vue) {
Vue.component(agMultionDatePicker.name, agMultionDatePicker)
}
// 导出组件
export default agMultionDatePicker;

View File

@ -0,0 +1,238 @@
<template>
<div class="ag-MultifunctionSearch">
<ag-select
v-bind="$attrs"
v-on="listeners"
:options="$attrs.options||[]"
:value="value&&value[0]"
@change="onSelect"
class="ag_select_group"
:style="{ width: `${swidth}px` }"
>
</ag-select>
<ag-date-picker
class="ag_input_group"
:placeholder="$attrs.placeholder||'请选择时间'"
:disabled="$attrs.disabled"
v-bind="$attrs"
v-on="listeners"
:value="value[1]"
@change="onInputChange"
>
</ag-date-picker>
</div>
</template>
<script>
import agSelect from "../../agSelect/src/index.vue";
import agDatePicker from "../../agDatePicker/src/index.vue";
export default {
name: "agMultionDatePicker",
components: {
agSelect,
agDatePicker,
},
props: {
value: {
default: () => {
return [null, []];
},
},
},
data() {
return {
values: [null, []],
swidth:100,
inputValue:"",
selectvalue:"",
imeipopover:false,
listeners:{}
};
},
model: {
prop: 'value', // prop 'value'
event: 'change' // value
},
watch: {
value: {
handler(newVal) {
if (!Array.isArray(newVal)) {
throw new Error("请传入数组");
}
let newselectValue=newVal[0];
const find=this.$attrs.options&&this.$attrs.options.find((f)=>f.value==newselectValue);
if(find){
let fontwidth=this.getStringWidth(find.label);
this.swidth=fontwidth>165?165:fontwidth;
}else{
this.swidth=100;
}
},
immediate: true,
},
},
mounted(){
let newlist=Object.assign({},this.$listeners);
delete newlist.change;
delete newlist.input;
this.listeners=newlist;
},
methods: {
onSelect(value){
if(this.$listeners.change){
this.$emit("change", [value, this.value[1]||[]]);
}
},
onInputChange(val){
if(this.$listeners.change){
this.$emit("change", [this.value[0] ||"",val]);
}
},
getStringWidth(text) {
let font = "13px";
//
let element = document.createElement('span');
element.style.visibility = 'hidden';
element.style.whiteSpace = 'nowrap';
element.style.font = font; //
element.textContent = text;
//
document.body.appendChild(element);
//
let width = element.offsetWidth;
//
document.body.removeChild(element);
return width+50;
},
imeiiconClick(){
this.imeipopover=!this.imeipopover;
},
imeiclose(){
this.imeipopover=false;
},
cleartext(){
this.value[1]=[];
this.$set(this.value,1,[]);
},
search(){
this.imeipopover=false;
if(this.$listeners.onSrarch){
this.$emit("onSrarch", this.value);
}
},
getAfterAgo(attr, prefix) {
const config = {};
for (const key in attr) {
if (attr.hasOwnProperty(key) && key.startsWith(prefix)) {
const newKey = key.substring(prefix.length);
config[newKey] = attr[key];
}
}
return config;
},
},
};
</script>
<style lang="scss">
.popperOptions {
min-width: 200px !important;
padding: 0px !important;
}
</style>
<style scoped lang='scss'>
.ag-MultifunctionSearch {
display: flex;
align-items: center;
}
.imeiicondefault {
padding: 5px;
border-radius: 4px;
background: #f0f2f5;
cursor: pointer;
}
.imeiiconActive {
cursor: pointer;
color: #409eff;
background: #e9f1fc;
}
.divider {
margin: 0;
}
.btnbox {
padding: 5px;
display: flex;
justify-content: space-between;
}
::v-deep{
.ag_select_group{
.el-input__inner{
border:1px solid #e5e5e5;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
outline:none;
&:focus{
border-color:#e5e5e5;
}
}
}
.el-range-editor.is-active, .el-range-editor.is-active:hover, .el-select .el-input.is-focus .el-input__inner{
border-color:#e5e5e5;
}
.el-range-editor.el-input__inner{
border:1px solid #e5e5e5;
border-left:0px;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
border-left:none;
outline:none;
&:focus{
border-color:#e5e5e5;
}
}
.ag_input_group{
.el-input__inner{
border:1px solid #e5e5e5;
border-left:0px;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
border-left:none;
outline:none;
&:focus{
border-color:#e5e5e5;
}
}
}
.el-select{
.el-input{
&.is-focus{
.el-input__inner{
border-color:#e5e5e5;
}
}
}
&:hover{
.el-input__inner{
border-color:#e5e5e5;
}
}
}
.el-textarea__inner{
border:none;
}
.el-input__suffix-inner{
height:100%;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

View File

@ -0,0 +1,9 @@
import agMultionInput from './src'
// 为组件提供 install 安装方法,供按需引入
agMultionInput.install = function (Vue) {
Vue.component(agMultionInput.name, agMultionInput)
}
// 导出组件
export default agMultionInput

View File

@ -0,0 +1,294 @@
<template>
<div class="ag-MultifunctionSearch">
<ag-select
v-bind="$attrs"
v-on="listeners"
:options="$attrs.options||[]"
:value="value&&value[0]"
@change="onSelect"
class="ag_select_group"
:style="{ width: `${swidth+20}px` }"
>
</ag-select>
<ag-input
v-if="!showExtra"
class="ag_input_group"
:placeholder="$attrs.placeholder||'请输入内容'"
:disabled="$attrs.disabled||imeipopover"
v-bind="$attrs"
v-on="listeners"
:value="value[1]"
@input="onInputChange"
>
<template slot="prefix" >
</template>
</ag-input>
<el-popover
v-if="showExtra"
placement="bottom"
trigger="manual"
ref="popover"
popper-class="popperOptions"
v-model="imeipopover"
>
<template slot="reference">
<ag-input
class="ag_input_group"
:placeholder="$attrs.placeholder||'请输入内容'"
:disabled="$attrs.disabled||imeipopover"
v-bind="$attrs"
v-on="listeners"
:value="value&&value[1]"
@input="onInputChange"
>
<template slot="suffix" v-if="showExtra">
<i
class="iconfont icon-icon-piliangcaozuo imeiicondefault"
:class="imeipopover && 'imeiiconActive'"
style="font-size: 12px;"
@click.prevent="imeiiconClick"
></i>
</template>
</ag-input>
</template>
<el-input
type="textarea"
:rows="12"
:value="value&&value[1]"
placeholder="一行一项最多支持200行"
resize="none"
border="none"
class="textareainput"
@input="onInputChange"
/>
<divider class="divider"></divider>
<div class="btnbox">
<div>
<el-button size="mini" @click="imeiclose"
>关闭</el-button
>
</div>
<div>
<el-button size="mini" @click="cleartext"
>清空</el-button
>
<el-button
size="mini"
type="primary"
plain
@click="search"
>查询</el-button
>
</div>
</div>
</el-popover>
</div>
</template>
<script>
import agSelect from "../../agSelect/src/index.vue";
import agInput from "../../agInput/src/index.vue";
import {Divider} from "element-ui";
export default {
name: "agMultionInput",
components: {
agSelect,
agInput,
Divider
},
props: {
value: {
type: Array,
default: () => {
return [null, ''];
},
},
showExtra: {
type: Boolean,
default: false,
},
},
data() {
return {
values: [null, ""],
swidth:120,
inputValue:"",
selectvalue:"",
imeipopover:false,
listeners:{}
};
},
model: {
prop: 'value', // prop 'value'
event: 'change' // value
},
watch: {
value: {
handler(newVal) {
if (!Array.isArray(newVal)) {
throw new Error("请传入数组");
}
let newselectValue=newVal[0];
const find=newselectValue&&newselectValue!=""&&this.$attrs.options&&this.$attrs.options.find((f)=>f.value==newselectValue);
if(find){
let fontwidth=this.getStringWidth(find.label);
this.swidth=fontwidth>165?165:fontwidth;
}else{
this.swidth=120;
}
},
immediate: true,
},
},
mounted(){
let newlist=Object.assign({},this.$listeners);
delete newlist.change;
delete newlist.input;
this.listeners=newlist;
},
methods: {
onSelect(value){
if(this.$listeners.change){
this.$emit("change", [value||"", this.value[1]||""]);
}
},
onInputChange(val){
if(this.$listeners.change){
this.$emit("change", [this.value[0]||"",val]);
}
},
getStringWidth(text) {
let font = "13px";
//
let element = document.createElement('span');
element.style.visibility = 'hidden';
element.style.whiteSpace = 'nowrap';
element.style.font = font; //
element.textContent = text;
//
document.body.appendChild(element);
//
let width = element.offsetWidth;
//
document.body.removeChild(element);
return width+50;
},
imeiiconClick(){
this.imeipopover=!this.imeipopover;
},
imeiclose(){
this.imeipopover=false;
},
cleartext(){
this.value[1]="";
this.$set(this.value,1,"");
},
search(){
this.imeipopover=false;
if(this.$listeners.onSrarch){
this.$emit("onSrarch", this.value);
}
},
getAfterAgo(attr, prefix) {
const config = {};
for (const key in attr) {
if (attr.hasOwnProperty(key) && key.startsWith(prefix)) {
const newKey = key.substring(prefix.length);
config[newKey] = attr[key];
}
}
return config;
},
},
};
</script>
<style lang="scss">
.popperOptions {
min-width: 200px !important;
padding: 0px !important;
}
</style>
<style scoped lang='scss'>
.ag-MultifunctionSearch {
display: flex;
align-items: center;
}
.imeiicondefault {
padding: 5px;
border-radius: 4px;
background: #f0f2f5;
cursor: pointer;
}
.imeiiconActive {
cursor: pointer;
color: #409eff;
background: #e9f1fc;
}
.divider {
margin: 0;
}
.btnbox {
padding: 5px;
display: flex;
justify-content: space-between;
}
::v-deep{
.ag_select_group{
.el-input__inner{
border:1px solid #e5e5e5;
border-top-right-radius: 0;
border-bottom-right-radius: 0;
outline:none;
&:focus{
border-color:#e5e5e5;
}
}
}
.ag_input_group{
.el-input__inner{
border:1px solid #e5e5e5;
border-bottom-left-radius: 0;
border-top-left-radius: 0;
border-left:none;
outline:none;
&:focus{
border-color:#e5e5e5;
}
}
}
.el-select{
.el-input{
&.is-focus{
.el-input__inner{
border-color:#e5e5e5;
}
}
}
&:hover{
.el-input__inner{
border-color:#e5e5e5;
}
}
}
.el-textarea__inner{
border:none;
}
.el-input__suffix-inner{
height:100%;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

View File

@ -0,0 +1,9 @@
import agMutionCheckbox from './src';
// 为组件提供 install 安装方法,供按需引入
agMutionCheckbox.install = function (Vue) {
Vue.component(agMutionCheckbox.name, agMutionCheckbox);
}
// 导出组件
export default agMutionCheckbox;

View File

@ -0,0 +1,430 @@
<template>
<div class="ag-multion-checkbox">
<el-dropdown
size="small"
trigger="click"
ref="multioncheckref"
@click.native="checkeddown"
:class="{ 'el-dropdown_active': !down }"
@visible-change="dropdownchange"
>
<span class="el-dropdown-link">
<span>
<span :class="{ color: checkedList.length }">{{ checkedList.length==0?placeholder||'请选择':dropdownvalue }}</span
><span class="list-length" v-if="valuelength >= 1"
>+ {{ valuelength }}</span
></span
><i
v-if="dropdownvalue == ''"
class="el-icon-arrow-up el-icon--right"
:class="{ 'rotate-arrow': down }"
></i>
<i v-else class="el-icon-close el-icon--right" @click.stop.prevent="clear"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-input
placeholder="请输入内容"
v-model="seek"
size="small"
class="el_input"
@input="inputblur"
@keydown.enter.prevent="confirm()"
>
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
<div v-if="dataList && dataList.length" class="checkbox-group">
<div v-if="dataList && dataList.length > 1" class="all-checked">
<el-checkbox
:indeterminate="isIndeterminate"
v-model="checkAll"
@change="handleCheckAllChange"
>全选</el-checkbox
>
/
<span class="invertstatus" @click="handleCheckAllChange(!checkAll)">反选</span>
</div>
<el-checkbox-group
class="checked-list"
:class="{ 'top-height': dataList && dataList.length <= 1 }"
v-model="checkedList"
@change="handleCheckedCitiesChange"
>
<div v-for="(el, id) in dataList" :key="id" class="setScore">
<el-checkbox :label="el.value">{{ el.label }}</el-checkbox>
<span @click="confirm(el, 'only')">仅筛选此项</span>
</div>
</el-checkbox-group>
</div>
<div v-else class="nothing">暂无数据</div>
<div class="btnbox">
<div>
<el-button size="mini" @click="cancel">取消</el-button>
<el-button size="mini" type="primary" plain @click="confirm()"
>确定</el-button
>
</div>
</div>
</el-dropdown-menu>
</el-dropdown>
</div>
</template>
<script>
import { nextTick } from 'vue';
export default {
props: ["datas", "placeholder", "value"],
name: "AgMutionCheckbox",
data() {
return {
seek: "",
dropdownvalue:"",
valuelength: 0,
down: true,
checkAll: false,
checkedList: [],
dataList: [
], //
isIndeterminate: false,
retract: false,
olddatas:[
],
oldvalue:[]
};
},
model: {
prop: 'value', // prop 'value'
event: 'onSearch' // value
},
mounted() {
if(this.datas&&Array.isArray(this.datas)){
this.dataList = this.datas;
this.olddatas=Object.assign([],this.datas);
this.checkedList = []; //-
if (this.checkAll && this.dataList) {
this.checkedList=this.dataList.map((e) =>e.value);
this.confirm();
}
if(this.value){
this.checkedList=this.value;
const filters=this.olddatas.filter((f)=>{
return this.checkedList.find((c)=>c==f.value);
});
if(filters.length>0){
const [onedatas,...otherdatas]=filters;
this.valuelength=otherdatas.length;
this.dropdownvalue=onedatas.label;
if(filters.length==this.olddatas.length){
this.checkAll=true;
this.isIndeterminate=false;
}else{
this.checkAll=false;
this.isIndeterminate=true;
}
}else{
this.valuelength=0;
this.dropdownvalue="";
this.checkAll=false;
this.isIndeterminate=false;
}
}
}else{
this.olddatas=Object.assign([],this.dataList);
}
},
watch: {
datas: {
handler(val) {
if(val&&Array.isArray(val)){
this.dataList = val;
this.olddatas=Object.assign([],val);
this.checkedList = []; //-
if (this.checkAll&&this.dataList) {
this.checkedList=this.dataList.map((e) =>e.value);
this.confirm();
}
if(this.value){
this.checkedList=this.value;
const filters=this.olddatas.filter((f)=>{
return this.checkedList.find((c)=>c==f.value);
});
if(filters.length>0){
const [onedatas,...otherdatas]=filters;
this.valuelength=otherdatas.length;
this.dropdownvalue=onedatas.label;
if(filters.length==this.olddatas.length){
this.checkAll=true;
this.isIndeterminate=false;
}else{
this.checkAll=false;
this.isIndeterminate=true;
}
}else{
this.valuelength=0;
this.dropdownvalue="";
this.checkAll=false;
this.isIndeterminate=false;
}
}
} else{
this.olddatas=Object.assign([],this.dataList);
}
},
deep: true,
immediate: true,
},
},
methods: {
clear(){
this.checkedList = [];
this.valuelength=0;
this.dropdownvalue="";
this.isIndeterminate = false;
this.checkAll=false;
this.nextTick(()=>{
this.$refs.multioncheckref.hide();
});
},
checkeddown(val){
this.oldvalue={list:this.checkedList,checkAll:this.checkAll,isIndeterminate:this.isIndeterminate};
},
dropdownchange(){
this.down = !this.down;
},
inputblur(val){
this.dataList=this.olddatas.filter((f)=>f.label.indexOf(val)>-1);
},
handleCheckAllChange(val){
this.checkedList = val ? this.olddatas.map((h)=>h.value) : [];
this.isIndeterminate = false;
this.checkAll=val;
},
confirm(e,type){
if(e&&type=="only"){
this.checkedList=[e.value];
if(this.olddatas.length!=1){
this.isIndeterminate=true;
this.checkAll=false;
}else{
this.isIndeterminate=false;
this.checkAll=true;
}
const filters=this.olddatas.filter((f)=>{
return f.value==e.value
});
if(this.$listeners.onSearch){
this.$emit("onSearch",[e.value]);
}
if(filters.length==0){
this.valuelength=0;
this.dropdownvalue="";
this.$refs.multioncheckref.hide();
return;
}
const [onedatas,...otherdatas]=filters;
this.valuelength=otherdatas.length;
this.dropdownvalue=onedatas.label;
}else{
if(this.checkedList.length==0){
this.valuelength=0;
this.dropdownvalue="";
if(this.$listeners.onSearch){
this.$emit("onSearch",this.checkedList);
}
this.$refs.multioncheckref.hide();
return;
}
const filters=this.olddatas.filter((f)=>{
return this.checkedList.find((c)=>c==f.value);
});
const [onedatas,...otherdatas]=filters;
this.valuelength=otherdatas.length;
this.dropdownvalue=onedatas.label;
if(this.$listeners.onSearch){
this.$emit("onSearch",this.checkedList);
}
}
this.$refs.multioncheckref.hide();
},
handleCheckedCitiesChange(val){
if(val.length>0){
if(val.length==this.olddatas.length){
this.isIndeterminate = false;
this.checkAll=true;
}else{
this.isIndeterminate = true;
}
}else{
this.isIndeterminate = false;
this.checkAll=false;
}
},
cancel(){
this.checkedList=this.oldvalue.list;
this.isIndeterminate=this.oldvalue.isIndeterminate;
this.checkAll=this.oldvalue.checkAll;
this.$refs.multioncheckref.hide();
}
}
};
</script>
<style lang="scss" scoped>
::v-deep .el_input {
padding: 0 10px;
.el-input__inner {
border-radius: 0px;
border: none;
border-bottom: 1px solid #dcdfe6;
}
.el-input__inner:hover {
border-bottom: 1px solid #409eff;
}
}
.el-dropdown {
width: 100%;
height: 32px;
line-height: 32px;
border: 1px solid #dcdfe6;
border-radius: 4px;
}
.el-dropdown:hover {
border: 1px solid #c0c4cc;
}
.el-dropdown_active {
border: 1px solid #409eff;
}
.el-dropdown_active:hover {
border: 1px solid #409eff;
}
.el-icon-arrow-up {
transform: rotate(0deg);
transition: transform 0.3s ease;
}
.rotate-arrow {
transform: rotate(180deg);
transition: transform 0.3s ease;
}
.el-dropdown-menu {
min-width: 400px;
padding: 10px 0 0 0;
}
.checkbox-group {
overflow: hidden;
padding: 5px 0px 0;
box-sizing: border-box;
}
.all-checked {
span {
font-size: 14px;
color: #606266;
font-weight: 500;
cursor: pointer;
}
.invertstatus:hover {
color: #409eff;
}
}
.checked-list {
max-height: 180px;
margin-top:5px;
overflow-y: auto;
padding: 5px 0;
box-sizing: border-box;
}
.top-height {
margin-top: 0px;
}
.el-checkbox-group {
display: flex;
flex-direction: column;
}
.el-checkbox {
margin-right: 0px !important;
padding: 5px 0px 5px 10px;
box-sizing: border-box;
}
.btnbox {
padding: 5px;
display: flex;
justify-content: right;
border-top: 1px solid #dcdfe6;
}
.el-dropdown-link {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 10px;
box-sizing: border-box;
width: 100%;
height:100%;
min-width: 180px;
font-size: 13px;
font-weight: 500;
color: #c0c4cc;
cursor: pointer;
}
.list-length {
padding: 3px 7px;
margin-left: 10px;
border-radius: 4px;
color: #33333379;
background-color: #dcdfe6;
}
.color {
color: #333;
font-weight: normal;
}
:focus-visible {
outline: none;
}
.nothing {
min-height: 80px;
line-height: 80px;
text-align: center;
color: #c0c4cc;
}
.el-icon-close {
opacity: 0;
}
.el-icon-close:hover {
opacity: 1;
}
.setScore {
display: flex;
justify-content: space-between;
align-items: center;
width: 100%;
font-size: 14px;
span {
display: none;
height: 30px;
line-height: 30px;
padding: 0 10px;
cursor: pointer;
}
::v-deep .el-checkbox {
height: 30px;
line-height: 30px;
display: flex;
flex: 1;
align-items: center;
}
}
.setScore:hover {
background-color: rgba(192, 196, 204, 0.267);
span {
display: block;
color: #409eff;
height: 100%;
}
}
</style>

View File

@ -1,4 +1,4 @@
import agNumberRange from './src' import agNumberRange from './src';
// 为组件提供 install 安装方法,供按需引入 // 为组件提供 install 安装方法,供按需引入
agNumberRange.install = function (Vue) { agNumberRange.install = function (Vue) {

View File

@ -7,7 +7,7 @@
@input="handleStartInput" @input="handleStartInput"
@blur="handleInputBlur" @blur="handleInputBlur"
></el-input> ></el-input>
<span class="separator">{{ rangeSeparator }}</span> <span class="separator">{{ rangeSeparator||'-'}}</span>
<el-input <el-input
:style="{ width: width + 'px' }" :style="{ width: width + 'px' }"
:size="size" :size="size"
@ -63,6 +63,9 @@ export default {
// //
endValue: "", endValue: "",
}; };
},
mounted() {
}, },
watch: { watch: {
value(newValue) { value(newValue) {

View File

@ -10,17 +10,14 @@
<ag-row> <ag-row>
<ag-col :span="12" v-for="(item,index) in inputs" :key="index"> <ag-col :span="12" v-for="(item,index) in inputs" :key="index">
<el-form-item :label="item.label" :rules="item.rules" :prop="item.name"> <el-form-item :label="item.label" :rules="item.rules" :prop="item.name">
<ag-input :placeholder="item.placeholder" :value="item.value||''" closeable v-if="item.elem=='el-input'" @change="onSelect($event,item,index)"/> <ag-select :placeholder="item.placeholder" :value="item.value||''" closeable :options="item.options" style="width:100%" @change="onSelect($event,item,index)"/>
<ag-select :placeholder="item.placeholder" :value="item.value||''" closeable v-if="item.elem=='el-select'" :options="item.options" style="width:100%" @change="onSelect($event,item,index)"/>
</el-form-item>
</ag-col>
<ag-col :offset="16" :span="8">
<el-form-item >
<el-button size="small" @click="onCancel">取消</el-button>
<el-button size="small" type="primary" @click="onQuery">查询</el-button>
</el-form-item> </el-form-item>
</ag-col> </ag-col>
</ag-row> </ag-row>
<div class="footerbox">
<el-button size="small" @click="onCancel">取消</el-button>
<el-button size="small" type="primary" @click="onQuery">查询</el-button>
</div>
</el-form> </el-form>
</el-popover> </el-popover>
</div> </div>
@ -55,10 +52,28 @@
return []; return [];
}, },
}, },
props:{
type: Object,
default: () => {
return {
lazyLoad (node, resolve) {
}
};
},
}
},
model: {
prop: 'values', // prop 'value'
event: 'onSearch' // value
}, },
mounted(){ mounted(){
let newinput=this.inputs.map((g)=>g.label); let newinput=this.inputs.map((g)=>g.label);
this.placeholder="请选择"+newinput.join("/"); this.placeholder="请选择"+newinput.join("/");
if(this.props.lazyLoad){
this.props.lazyLoad({level:0,pathLabels:this.values,value:""},(datas)=>{
this.inputs[index+1].options=datas;
});
}
}, },
data() { data() {
return { return {
@ -66,14 +81,18 @@
placeholderValue:"", placeholderValue:"",
placeholder:"请选择", placeholder:"请选择",
popState:false, popState:false,
} }
}, },
methods: { methods: {
onReset(){
this.values={};
this.$emit('onSearch',this.values);
},
onSelect(value,item,index){ onSelect(value,item,index){
item.value=value; item.value=value;
this.$set(this.inputs,index,item); this.$set(this.inputs,index,item);
let newvalues={}; let newvalues={};
let newplaceholderValue=[]; let newplaceholderValue=[];
this.inputs.map((g)=>{ this.inputs.map((g)=>{
if(g.value&&g.name){ if(g.value&&g.name){
@ -81,24 +100,30 @@
g.options.find((f)=>f.value==g.value).label&&(newplaceholderValue.push(g.options.find((f)=>f.value==g.value).label)); g.options.find((f)=>f.value==g.value).label&&(newplaceholderValue.push(g.options.find((f)=>f.value==g.value).label));
} }
}); });
this.$emit('change',newvalues);
this.values=newvalues; this.values=newvalues;
if(this.$listeners.change){
this.$emit('change',newvalues);
}
if(this.props.lazyLoad){
this.props.lazyLoad({level:index+1,pathLabels:newvalues,value},(datas)=>{
this.inputs[index+1].options=datas;
});
}
this.placeholderValue=newplaceholderValue.join("/") this.placeholderValue=newplaceholderValue.join("/")
}, },
onCancel(){ onCancel(){
this.popState=false; this.popState=false;
this.$emit('cancel'); if(this.$listeners.onCancel){
this.$emit('onCancel');
}
}, },
onQuery(){ onQuery(){
console.log(this.$refs.queryformref,'this.$refs.queryformref');
if(this.$refs.queryformref){ if(this.$refs.queryformref){
this.$refs.queryformref.validate((valid) => { this.$refs.queryformref.validate((valid) => {
console.log(valid,"valid");
if (valid) { if (valid) {
this.$emit('onSearch',this.values); this.$emit('onSearch',this.values);
this.popState=false; this.popState=false;
} else { } else {
console.log('error submit!!');
return false; return false;
} }
}); });
@ -120,6 +145,12 @@
caret-color: transparent; /* 隐藏光标 */ caret-color: transparent; /* 隐藏光标 */
} }
} }
.footerbox{
display: flex;
align-items: center;
justify-content: flex-end;
margin-top:5px;
}
::v-deep { ::v-deep {
.el-form-item--small .el-form-item__label{ .el-form-item--small .el-form-item__label{
line-height: 40px; line-height: 40px;

View File

@ -0,0 +1,9 @@
import agSearch from './src';
// 为组件提供 install 安装方法,供按需引入
agSearch.install = function (Vue) {
Vue.component(agSearch.name, agSearch);
}
// 导出组件
export default agSearch;

View File

@ -0,0 +1,229 @@
<template>
<el-form v-bind="$attrs" v-on="$listeners" :model="formValue" ref="formref">
<ag-row class="searchbox">
<ag-col v-for="item,index in data" :key="index" :span="item.span||24">
<el-form-item :prop="item.enName" >
<slot :name="item.soltName" v-if="item.soltName" :row="item"/>
<div class="contentbox" v-else>
<ag-input v-model="formValue[item.enName]" type="textarea" v-bind="item" v-if="item.type=='ag-textarea'" size="small" clearable></ag-input>
<ag-input v-model="formValue[item.enName]" v-bind="item" v-if="item.type=='ag-input'" size="small" clearable></ag-input>
<ag-date-picker v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-date-picker'" size="small" clearable/>
<ag-select v-model="formValue[item.enName]" v-bind="item" v-if="item.type=='ag-select'" size="small" clearable/>
<el-radio-group v-model="formValue[item.enName]" v-bind="item" v-if="item.type=='ag-radio'&&formValue[item.enName]" size="small" clearable>
<el-radio :label="it.value" v-for="it,index in item.options" :key="index">{{it.label}}</el-radio>
</el-radio-group>
<el-checkbox-group v-bind="item" v-if="item.type=='ag-checkbox'&&formValue[item.enName]" v-model="formValue[item.enName]" size="small" clearable>
<el-checkbox v-for="it,index in item.options" :label="it.value" :key="index">{{it.label}}</el-checkbox>
</el-checkbox-group>
<el-input-number v-model="formValue[item.enName]" v-bind="item" v-if="item.type=='ag-input-number'" clearable></el-input-number>
<el-cascader v-model="formValue[item.enName]" v-bind="item" size="small" v-if="item.type=='ag-cascader'" clearable :style="{width:'100%'}"></el-cascader>
<el-switch
v-model="formValue[item.enName]"
v-bind="item" size="small" v-if="item.type=='ag-switch'&&formValue[item.enName]" >
</el-switch>
<el-slider v-model="formValue[item.enName]"
v-bind="item" size="small" v-if="item.type=='ag-slider'"></el-slider>
<el-time-select
:style="{width:'100%'}"
clearable
v-model="formValue[item.enName]"
v-bind="item" size="small"
v-if="item.type=='ag-time-select'"
:picker-options="item.options">
</el-time-select>
<ag-upload v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-upload'" clear isPre></ag-upload>
<el-rate v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-rate'" size="small"></el-rate>
<agColorPicker v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-color-picker'" size="small">
</agColorPicker>
<el-transfer :data="item.data" v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-transfer'" size="small"></el-transfer>
<agNumberRange v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-numberRange'" size="small" clearable/>
<ag-multion-input v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-multion-input'" size="small" clearable/>
<agQuery v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-query'" size="small" clearable/>
<ag-mution-checkbox :datas="item.datas" v-bind="item" :placeholder="item.placeholder" v-model="formValue[item.enName]" v-if="item.type=='ag-mution-checkbox'" size="small"> </ag-mution-checkbox>
<ag-multion-date-picker v-bind="item" v-model="formValue[item.enName]" v-if="item.type=='ag-multion-datte-picker'" size="small"/>
</div>
</el-form-item>
</ag-col>
<ag-col :span="4">
<el-button @click="onSubmit" type="primary" size="small" class="submitbtn">
{{submittitle||'查询'}}
</el-button>
<el-button @click="onRest" size="small" class="submitbtn">
重置
</el-button>
</ag-col>
</ag-row>
</el-form>
</template>
<script>
import agInput from './../../agInput/src/index.vue';
import agDatePicker from './../../agDatePicker/src/index.vue';
import agSelect from './../../agSelect/src/index.vue';
import agRow from './../../agRow/src/index.vue';
import agCol from './../../agCol/src/index.vue';
import agUpload from './../../agUpload/src/index.vue';
import agColorPicker from './../../agColorPicker/src/index.vue';
import agNumberRange from './../../agNumberRange/src/index.vue';
import agMultionInput from './../../agMultionInput/src/index.vue';
import agQuery from './../../agQuery/src/index.vue';
import agMultionDatePicker from '../../agMultionDatePicker';
export default{
name:"agSearch",
components:{
agMultionDatePicker,
agInput:agInput,
agDatePicker,
agSelect,
agRow,
agCol,
agUpload:agUpload,
agColorPicker,
agNumberRange,
agMultionInput,
agQuery
},
props:{
submittitle:{
type:String,
default:'查询'
},
data:{
type:Array,
default(){
return []
}
},
span:{
type:Number,
default:24
},
},
data(){
return{
formValue:{
}
}
},
model:{
prop:'value',
event:{
'onSubmit':'submit',
'onRest':'rest'
}
},
mounted(){
this.formValue=this.$attrs.value;
setTimeout(()=>{
this.$refs.formref.clearValidate();
},1);
if(this.$refs.formref){
for(const key in this.$refs.formref){
if(!this[key]&&key!='value'){
this[key]=this.$refs.formref[key];
}
}
}
},
watch:{
value(val){
this.formValue = val;
setTimeout(()=>{
this.$refs.formref.clearValidate();
},1);
}
},
methods:{
onSubmit(){
if(this.$listeners.onSearch){
this.$emit('onSearch',this.formValue);
}
},
onRest(){
Object.keys(this.formValue).map((key,index)=>{
let itemvalue=Object.values(this.formValue)[index];
if(typeof(itemvalue)=="string"||typeof(itemvalue)=="number"){
this.formValue[key]=null;
}else{
this.formValue[key]=[];
}
});
setTimeout(()=>{
this.$refs.formref.clearValidate();
},1);
if(this.$listeners.onRest){
this.$emit('onRest',this.formValue);
}
}
},
}
</script>
<style lang="scss" scoped>
.searchbox{
align-items: center;
}
.footerbox{
margin-top:12px;
display:flex;
justify-content:center;
.submitbtn{
width:200px;
}
}
.el-form--inline{
.el-form-item{
display:flex;
}
.el-form-item__content{
flex:1;
width:100%;
}
}
.contentbox{
width:100%;
display: flex;
align-items: stretch;
}
.flexcenter{
display: flex;
align-items: stretch;
}
::v-deep{
.el-form--inline{
.el-form-item{
display:flex;
align-items: center;
margin-bottom: 0px;
}
.el-form-item__content{
display:flex;
flex:1;
width:100%;
margin-left:0;
align-items: center;
margin-bottom: 0px;
}
}
.el-form-item{
width:100%;
margin-bottom: 0px;
line-height:1;
}
.el-form-item__content{
flex:1;
display:flex;
width:100%;
align-items: center;
margin-left:0!important;
margin-bottom: 0px;
line-height:1;
}
}
</style>

View File

@ -1,23 +1,21 @@
<template> <template>
<div>
<el-select <el-select
class="ag_select" class="ag_select"
:style="{ width: `100%` }" :style="{ width: `100%` }"
:value="selectValue" :value="selectValue"
v-bind="attrs" ref="apselectref"
:size="$attrs.size||'small'"
v-bind="$attrs"
v-on="$listeners" v-on="$listeners"
> >
<slot /> <slot v-for="(_,name) in $slots" :name="name" :slot="name"> </slot>
<slot name="prefix" slot="prefix" />
<slot name="empty" slot="empty" />
<el-option <el-option
v-for="item in options" v-for="item in options"
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
></el-option> ></el-option>
</el-select> </el-select>
</div>
</template> </template>
<script> <script>
@ -43,6 +41,15 @@ export default {
selectValue:"" selectValue:""
}; };
}, },
mounted(){
if(this.$refs.apselectref){
for(const key in this.$refs.apselectref){
if(!this[key]&&key!='value'){
this[key]=this.$refs.apselectref[key];
}
}
}
},
watch: { watch: {
value: { value: {
handler(newVal) { handler(newVal) {
@ -57,16 +64,6 @@ export default {
immediate: true, immediate: true,
}, },
}, },
computed: {
attrs() {
return {
size: "small",
clearable: true,
filterable: true,
...this.$attrs,
};
},
},
methods: { methods: {
}, },
}; };
@ -76,6 +73,7 @@ export default {
::v-deep { ::v-deep {
.ag_select{ .ag_select{
min-width: 90px; min-width: 90px;
width:100%;
} }
.el-input__icon { .el-input__icon {
font-size: 16px !important; font-size: 16px !important;

View File

@ -0,0 +1,9 @@
import agTable from './src';
// 为组件提供 install 安装方法,供按需引入
agTable.install = function (Vue) {
Vue.component(agTable.name, agTable)
}
// 导出组件
export default agTable;

View File

@ -0,0 +1,90 @@
<template>
<div class="agtablebox">
<el-table v-bind="$attrs" v-on="$listeners" :data="data" size="small" :header-cell-style="$attrs.headerCellStyle||{backgroundColor:'#f7f7f7'}" ref="tableref">
<el-table-column v-for="item,index in columns" :key="index" v-bind="item">
<template slot-scope="scope">
<div v-if="item.soltName">
<slot :name="item.soltName" :row="scope.row" >
</slot>
</div>
<div v-else>
{{scope.row[item.prop]}}
</div>
</template>
</el-table-column>
</el-table>
<div class="agtablefooter">
<el-pagination
ref="paginationref"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page.sync="currentPage"
:page-sizes="pageSizes||[10,100,200,500]"
:page-size="pageSize"
layout="sizes,prev, pager, next, jumper"
:total="total">
</el-pagination>
</div>
</div>
</template>
<script>
export default {
name: "agTable",
props:["columns","data",'pageSize','currentPage','total','pageSizes'],
data() {
return {
};
},
mounted() {
for(const key in this.$refs.tableref){
if(!this[key]&&key!='value'){
this[key]=this.$refs.tableref[key];
}
}
for(const key in this.$refs.paginationref){
if(!this[key]&&key!='value'&&key!='pageSizes'){
this[key]=this.$refs.paginationref[key];
}
}
},
methods: {
handleSizeChange(e){
if(this.$listeners.onSizeChange){
this.$emit('onSizeChange',e);
}
},
handleCurrentChange(e){
if(this.$listeners.onCurrentChange){
this.$emit('onCurrentChange',e);
}
}
},
};
</script>
<style lang="scss" scoped>
.agtablebox{
background: #fff;
width: 100%;
height:100%;
display: flex;
flex-direction: column;
.agtablefooter{
display: flex;
justify-content: flex-end;
padding:12px;
}
}
::v-deep {
.el-table {
width: 100%;
height:100%;
}
.el-table__empty-block{
min-height:300px;
}
}
</style>

View File

@ -6,4 +6,4 @@ agTabs.install = function (Vue) {
} }
// 导出组件 // 导出组件
export default agTabs export default agTabs;

View File

@ -1,5 +1,5 @@
<template> <template>
<el-tabs class="ag-tabs" v-bind="$attrs" v-on="$listeners"> <el-tabs class="ag-tabs" v-bind="$attrs" v-on="$listeners" ref="tabsref">
<slot v-if="!tabsData.length" name="default" /> <slot v-if="!tabsData.length" name="default" />
<el-tab-pane <el-tab-pane
v-else v-else
@ -31,6 +31,13 @@ export default {
}, },
}, },
}, },
mounted() {
for(const key in this.$refs.tabsref){
if(!this[key]&&key!='value'){
this[key]=this.$refs.tabsref[key];
}
}
},
data() { data() {
return {}; return {};
}, },

View File

@ -0,0 +1,9 @@
import agUpload from './src'
// 为组件提供 install 安装方法,供按需引入
agUpload.install = function (Vue) {
Vue.component(agUpload.name, agUpload)
}
// 导出组件
export default agUpload;

View File

@ -0,0 +1,692 @@
<template>
<div class="uploadbox">
<div class="uploadimgbox" :style="{height:width+'px'}" v-if="listType=='picture-card'">
<div v-for="img,index in imageList" :key="index" :style="{width:width+'px'}" :draggable="dragmove" class="imgitem" :class="dragmove&&dragmoveclass" @dragstart="onDragstart" @dragenter="onDragenter" @dragover="onDragover" @dragend="onDragend" @dragleave="onMouseup" >
<slot name="file" :file="img">
<img :src="img.url" class="img" v-if="img.type.indexOf('image')>-1"/>
<div v-else class="filetext">
<div class="filetitletext">文件</div>
<div>{{img.name}}</div>
</div>
<div class="uploadimgmask" v-if="clear||isPrew" >
<i class="el-icon-search" v-if="isPrew&&img.type.indexOf('image')>-1" @click="handlePictureCardPreview(img)"></i>
<i class="el-icon-delete" v-if="clear" @click="handleRemove(img.raw,index)"></i>
</div>
</slot>
</div>
<div class="uploadcontain" :style="{height:width+'px',aspectRatio:isDrap?4/3:1/1}"
:draggable="isDrap"
@dragover.prevent
@dragenter.prevent
@drop="onUploadDrap"
v-if="limit>imageList.length">
<label for="uploadinput" class="uploadlabel" @click="onInputClick">
<slot>
<i class="el-icon-plus"></i>
<span class="draptiptext">{{isDrap?'拖拽上传':'上传'}}</span>
</slot>
</label>
</div>
</div>
<div class="uploadtextbox" v-if="listType=='text'||listType=='picture'">
<div class="uploadtextcontain"
:draggable="isDrap"
@dragover.prevent
@dragenter.prevent
@drop="onUploadDrap"
>
<label for="uploadinput" class="uploadlabel" @click="onInputClick" >
<slot>
<el-button type="primary" size="small"><i class="el-icon-plus"></i> {{isDrap?'拖拽上传':'上传'}}</el-button>
</slot>
</label>
</div>
<slot name="tip">
</slot>
<ul class="uploadtextlist" :class="'upload-list--'+listType">
<li v-for="img,index in imageList" :key="index" class="uploadtextitem" >
<div class="ag-upload-list__item-namebox" @click="handlePictureCardPreview(img)">
<slot name="icon">
<i class="el-icon-document ag-icon-document"></i>
</slot>
<slot name="file" :file="img">
<img v-if="listType=='picture'" :src="img.url" class="ag-upload-list__item-thumbnail" />
<a class="ag-upload-list__item-name" >{{img.name}}</a>
</slot>
</div>
<div class="uploadrightbox">
<slot name="status" :file="img">
<span class="uploadstatusbpx">
<i v-if="img.status==='uploading'" class="el-icon-loading uploadingicon" ></i>
<i v-if="img.status==='success'" class="el-icon-circle-check statusicon"></i>
<i v-if="img.status==='failed'" class="el-icon-circle-close failedicon"></i>
<span v-if="img.msg" class="uploadmsg">{{img.msg}}</span>
</span>
</slot>
<el-popconfirm
:title="`确定移除${img.name}吗?`"
@confirm="handleRemove(img.raw,index)"
>
<i class="el-icon-close ag-icon-close" slot="reference"></i>
</el-popconfirm>
</div>
</li>
</ul>
</div>
<input type="file" style="display:none;" :multiple="limit>1&&$attrs.multiple" :accept="$attrs.accept" id="uploadinput" @change="onUpdate" ref="fileinputref"/>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
</div>
</template>
<script>
export default {
name: "agUpload",
props:{
isPrew:{
type:Boolean,
default:true
},
clear:{
type:Boolean,
default:true
},
width:{
type:Number,
default:80
},
dragmove:{
type:Boolean,
default:false
},
isDrap:{
type:Boolean,
default:false
},
limit:{
type:Number,
default:1
},
fileList:{
type:Array,
default(){
return [];
}
},
listType:{
type:String,
default:'picture-card'
}
},
model: {
prop: 'fileList', // prop 'value'
event: 'onUpload' // value
},
mounted() {
if(this.fileList.length>0){
let list=[];
this.fileList.map((h)=>{
if(typeof(h)=='object'){
list.push(h);
}else{
let imgurl=h.split("/");
let filename=imgurl[imgurl.length-1];
let extarr=h.split(".");
let extname=extarr[extarr.length-1];
if(['jpg','jpeg','png','gif','svg'].indexOf(extname)>-1){
list.push({
name:filename,
url:h,
type:'image/'+extname.toLowerCase(),
raw:null
});
}else{
list.push({
name:filename,
url:h,
type:'other/'+extname.toLowerCase(),
raw:null
});
}
}
});
this.imageList=list;
}
},
watch:{
fileList(val){
if(val.length>0){
let list=[];
val.map((h)=>{
if(typeof(h)=='object'){
list.push(h);
}else{
let imgurl=h.split("/");
let filename=imgurl[imgurl.length-1];
let extarr=h.split(".");
let extname=extarr[extarr.length-1];
if(['jpg','jpeg','png','gif','svg'].indexOf(extname)>-1){
list.push({
name:filename,
url:h,
type:'image/'+extname.toLowerCase(),
raw:null
});
}else{
list.push({
name:filename,
url:h,
type:'other/'+extname.toLowerCase(),
raw:null
});
}
}
});
this.imageList=list;
}
}
},
data() {
return {
dialogImageUrl: '',
dialogVisible: false,
imageList:[],
sourceNode:null,
partNode:null,
selectTop:false,
drag:false,
dragmoveclass:'imghover'
};
},
methods: {
handleRemove(file,index) {
this.imageList=this.imageList.filter((item,inkey)=>{
return inkey !== index;
});
this.$emit('onRemove',file,this.imageList);
},
handlePictureCardPreview(file) {
if(file.type.indexOf('image/')>-1){
this.dialogImageUrl = file.url;
this.dialogVisible = true;
}
},
onUpdate(e){
if(this.limit<=this.imageList.length){
return;
}
try{
let uplist=[];
let files=[...e.target.files].slice(0,this.limit-this.imageList.length);
const find=files.find((f,i)=>{
if(this.$attrs.accept){
let acceptarr=this.$attrs.accept.split(",");
if(!acceptarr.includes(f.type)){
if(this.$listeners.onError){
this.$emit('onError',`其中第${i+1}张文件类型错误`);
}
return true;
}
}
if(this.$attrs.maxSize){
if(f.size>this.$attrs.maxSize){
if(this.$listeners.onError){
this.$emit('onError',`其中第${i+1}张文件大小不能超过${this.$attrs.maxSize/1024/1024}M"`);
}
return true;
}
}
});
if(find){
return;
}
files.map((item,index) => {
if(item.type.indexOf("image")>-1){
let filerender=new FileReader();
filerender.onload=function(e){
uplist.push({
name:item.name,
url:e.target.result,
type:item.type,
size:item.size,
raw:item,
status:'init',
msg:""
});
if(files.length==uplist.length){
if(this.$listeners.onUpload){
this.fileList.push(...uplist);
this.$emit('onUpload',this.fileList,uplist);
}else{
this.imageList.push(...uplist);
}
}
}.bind(this);
filerender.readAsDataURL(item);
}else{
uplist.push({
name:item.name,
type:item.type,
size:item.size,
raw:item,
status:'init',
msg:""
});
if(files.length==uplist.length){
if(this.$listeners.onUpload){
this.fileList.push(...uplist);
this.$emit('onUpload',this.fileList,uplist);
}else{
this.imageList.push(...uplist);
}
}
}
});
}catch(err){
console.log(err,"uploaderr");
if(this.$listeners.onError){
this.$emit('onError',e);
}
}
},
onDragstart(e){
this.sourceNode=e.target;
this.partNode=e.target.parentNode;
e.target.classList.add("tabsbg");
setTimeout(()=>{
e.target.classList.remove("tabsbg");
e.target.classList.add("moving");
},1);
},
onDragenter(e){
e.preventDefault();
if(!this.dragmove){
return;
}
if(this.partNode===e.target||e.target===this.sourceNode){
return
}
const childrens=[...this.partNode.children];
let thistarget=childrens.find((f)=>f==e.target);
if(!thistarget){
thistarget=e.target.parentNode;
}
if(!thistarget){
return
}
const sourceIndex=childrens.indexOf(this.sourceNode);
const targetIndex=childrens.indexOf(thistarget);
if(sourceIndex<targetIndex){
this.partNode.insertBefore(this.sourceNode,thistarget.nextElementSibling);
}else{
this.partNode.insertBefore(this.sourceNode,thistarget);
}
},
onDragover(e){
e.preventDefault();
},
onMouseup(e){
e.preventDefault();
if(!this.dragmove){
return;
}
if(this.partNode===e.target||e.target===this.sourceNode){
return
}
this.sourceNode.classList.remove("moving");
},
onDragend(e){
e.preventDefault();
if(!this.dragmove){
return;
}
e.target.classList.remove("moving");
e.target.classList.remove("tabsbg");
this.sourceNode.classList.remove("moving");
if(this.partNode===e.target||e.target===this.sourceNode){
return
}
const childrens=[...this.partNode.children];
const sourceIndex=childrens.indexOf(this.sourceNode);
const item = this.imageList.splice(sourceIndex, 1)[0];
this.imageList.splice(sourceIndex, 0, item);
this.sourceNode=null;
this.partNode=null;
},
onUploadDrap(e){
e.preventDefault(); //
e.stopPropagation(); //
if(this.drap){
return;
}
const files = event.dataTransfer.files;
this.onUpdate({target:{files: files}});
},
onInputClick(e){
e.preventDefault(); //
e.stopPropagation(); //
this.$refs.fileinputref.click();
}
},
};
</script>
<style lang="scss" scoped>
.uploadcontain{
position: relative;
width:100px;
aspect-ratio: 1/1;
border-radius: 4px;
background-color: #fff;
cursor: pointer;
overflow: hidden;
.uploadlabel{
font-size: 16px;
color:#97a8be;
display: block;
width:100%;
cursor: pointer;
height:100%;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.draptiptext{
font-size:13px;
color:#666;
text-align: center;
margin-top: 5px;
}
}
}
.uploadimgbox{
position: relative;
width: auto;
display: flex;
align-items: center;
gap:5px;
.imgitem{
position: relative;
width:100px;
cursor: pointer;
background: #fff;
border-radius: 4px;
overflow: hidden;
aspect-ratio: 1/1;
transition: all .3s;
&.imghover:hover{
cursor: move;
}
&.tabsbg{
border-radius:6px 6px 0 0;
background:#fff;
overflow:hidden;
}
&.opcacityzero{
opacity: 0;
}
&.moving{
opacity: 0;
visibility: hidden;
}
img{
display: block;
aspect-ratio: 1/1;
width:100%;
}
.uploadimgmask{
position: absolute;
top:0;
display: none;
left: 0;
width:100%;
height:100%;
background-color: rgba(0,0,0,.3);
align-items: center;
justify-content: center;
gap:6px;
z-index: 1;
i{
font-size: 20px;
color:#fff;
cursor: pointer;
}
}
&:hover{
.uploadimgmask{
display: flex;
}
}
}
.filetext{
font-size:12px;
display: flex;
justify-content: center;
flex-direction: column;
padding:5px;
box-sizing: border-box;
overflow: hidden;
height:100%;
white-space: nowrap; /* 文本不换行 */
overflow: hidden; /* 隐藏溢出的内容 */
text-overflow: ellipsis;
text-align: center;
.filetitletext{
font-size:15px;
}
}
}
.uploadtextbox{
.uploadtextcontain{
margin-bottom:7px;
.uploadtextcontaindefaultbtn{
display:flex;
align-items:center;
gap:5px;
background: #fff;
width:fit-content;
padding:5px 12px;
box-sizing: border-box;
border-radius:4px;
font-size:13px;
}
}
.uploadtextlist{
display: block;
list-style-type: disc;
margin-inline-start: 0px;
margin-inline-end: 0px;
padding-inline-start: 40px;
unicode-bidi: isolate;
margin: 0;
padding: 0;
list-style: none;
margin-top:12px;
}
.upload-list--text{
margin: 0;
padding: 0;
list-style: none;
&:first-child{
margin-top: 10px;
}
.uploadtextitem{
position: relative;
list-style: none;
transition: all .5s cubic-bezier(.55,0,.1,1);
font-size: 14px;
color: #606266;
line-height:1;
margin-top: 5px;
position: relative;
display: flex;
padding: 5px 10px;
align-items: center;
justify-content: space-between;
box-sizing: border-box;
cursor: pointer;
border-radius: 4px;
width: 100%;
&:hover{
background-color: #f5f7fa;
.ag-icon-close{
display:inline-block;
}
}
}
.ag-upload-list__item-namebox{
margin-right: 40px;
padding-left: 4px;
line-height: 1.8;
}
.ag-upload-list__item-name{
text-decoration: none;
color: #606266;
display: block;
overflow: hidden;
text-overflow: ellipsis;
transition: color .3s;
white-space: nowrap;
font-size: 14px;
.ag-icon-document{
height: 100%;
margin-right: 7px;
color: #909399;
line-height: inherit;
&:hover{
color: #409eff;
cursor: pointer;
}
}
.el-icon-close{
position: relative;
right: 5px;
cursor: pointer;
opacity: .75;
color: #606266;
display:none;
}
}
}
.upload-list--picture{
margin: 0;
padding: 0;
list-style: none;
.uploadtextitem{
overflow: hidden;
position: relative;
z-index: 0;
background-color: #fff;
border: 1px solid #c0ccda;
border-radius: 6px;
box-sizing: border-box;
margin-top: 10px;
padding: 10px 10px 10px 90px;
height: 92px;
gap:10px;
display: flex;
align-items: center;
cursor: pointer;
&:hover{
.ag-icon-close{
display:inline-block;
}
}
.ag-upload-list__item-thumbnail{
vertical-align: middle;
display: inline-block;
width: 70px;
height: 70px;
float:left;
position: relative;
z-index: 1;
margin-left: -80px;
background: #fff;
}
.ag-upload-list__item-namebox{
margin-right: 40px;
padding-left: 4px;
line-height: 1.8;
}
.ag-upload-list__item-name{
flex:1;
color: #606266;
display: block;
overflow: hidden;
text-overflow: ellipsis;
transition: color .3s;
white-space: nowrap;
line-height: 70px;
font-size:14px;
margin-top: 0;
text-decoration: none;
.ag-icon-document{
display:none;
}
&:hover{
color: #409eff;
cursor: pointer;
}
}
.ag-icon-close{
position: absolute;
font-family: element-icons !important;
font-style: normal;
font-weight: 400;
font-variant: normal;
text-transform: none;
line-height: 1;
vertical-align: baseline;
display: none;
-webkit-font-smoothing: antialiased;
line-height: 1;
top: 5px;
right: 5px;
cursor: pointer;
opacity: .75;
color: #606266;
}
}
}
}
.uploadrightbox{
display:flex;
justify-content:flex-end;
align-items: center;
.uploadstatusbpx{
margin-right: 7px;
}
}
.uploadingicon{
color:#409eff;
}
.failedicon{
color:red;
}
.statusicon{
color:green;
}
.uploadmsg{
margin:0 7px;
}
::v-deep {
.el-table {
width: 100%;
}
}
</style>

View File

@ -1,11 +1,45 @@
// 导入组件 // 导入组件
import agInput from './agInput/index' import agInput from './agInput/index';
import agSelect from './agSelect/index' import agSelect from './agSelect/index';
import agUpload from './agUpload';
import agForm from './agForm';
import agColorPicker from './agColorPicker';
import agQuery from './agQuery';
import agTable from './agTable';
import agTabs from './agTabs';
import agDialog from './agDialog';
import agPagination from './agPagination';
import agRow from './agRow';
import agCol from './agCol';
import agMutionCheckbox from './agMutionCheckbox';
import agSearch from './agSearch';
import agDatePicker from "./agDatePicker";
import agNumberRange from "./agNumberRange";
import agMultionDatePicker from "./agMultionDatePicker";
import agMultionInput from "./agMultionInput";
// 注册组件
// 组件列表 // 组件列表
const components = [ const components = [
agInput, agInput,
agSelect agSelect,
agUpload,
agForm,
agColorPicker,
agQuery,
agTable,
agDialog,
agTabs,
agPagination,
agCol,
agRow,
agDatePicker,
agMutionCheckbox,
agSearch,
agNumberRange,
agMultionDatePicker,
agMultionInput
] ]
// 定义 install 方法,接收 Vue 作为参数(使用 use 注册插件,那么所有的组件都会被注册) // 定义 install 方法,接收 Vue 作为参数(使用 use 注册插件,那么所有的组件都会被注册)
@ -13,7 +47,9 @@
// 判断是否安装 // 判断是否安装
if (install.installed) return if (install.installed) return
// 遍历注册全局组件 // 遍历注册全局组件
components.map(component => Vue.component(component.name, component)) components.forEach(component => {
Vue.component(component.name, component);
});
} }
// 判断是否是直接引入文件 // 判断是否是直接引入文件
@ -22,11 +58,9 @@
} }
export default { export default {
version:'2.15.14',
// 导出的对象必须具有 install才能被 Vue.use() 方法安装 // 导出的对象必须具有 install才能被 Vue.use() 方法安装
install, install,
}
export {
// 以下是具体的组件列表 // 以下是具体的组件列表
agInput, ...components
agSelect
} }

View File

@ -2,15 +2,12 @@
<div> <div>
<el-button type="success" @click="abb = true">点击打开 Dialog</el-button> <el-button type="success" @click="abb = true">点击打开 Dialog</el-button>
<ag-dialog :visible.sync="abb"> </ag-dialog> <ag-dialog :visible.sync="abb"> </ag-dialog>
<ag-datePicker v-model="date_value" :showTime="true" :range="true" @change="onPicker"> <ag-date-picker v-model="date_value" :showTime="true" :range="true" @change="onPicker">
</ag-datePicker> </ag-date-picker>
<ag-input value="value"> </ag-input>
<ag-input v-model="inputValue" placeholder="请输入" @change="onInputChange" ref="aginputref">
</ag-input>
<ag-select :options="a_options" v-model="value"> <ag-select :options="a_options" v-model="value">
<!-- <template slot="prefix">
{{
(a_options.find((e) => e.value == value) || {}).label
}}
</template> -->
</ag-select> </ag-select>
<ag-NumberRange v-model="values"> </ag-NumberRange> <ag-NumberRange v-model="values"> </ag-NumberRange>
<el-form <el-form
@ -31,7 +28,7 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
<ag-MultifunctionSearch <ag-multion-input
size="small" size="small"
ref="ttt" ref="ttt"
v-model="values" v-model="values"
@ -39,12 +36,27 @@
:clearable="true" :clearable="true"
:options="a_options" :options="a_options"
@change="onInputinput" @change="onInputinput"
@blur="onblur"
> >
</ag-MultifunctionSearch> </ag-multion-input>
<ag-color-picker size="small" v-model="colorvalue"/>
<agQuery :inputs="inputs" @onSearch="onSearch"/> <agQuery :inputs="inputs" @onSearch="onSearch"/>
<agTable :columns="columns" :data="tabledata" :currentPage="2" :pageSize="100" :total="500" >
<template #name="{row}">
<div>{{row.name}}11</div>
</template>
<template #date="{row}">
<div>{{row.date}} </div>
</template>
</agTable>
<agUpload @onUpload="onUpload" isDrap dragmove multiple :limit="5" @onError="onError" :maxSize="1024*1024" :value="filelist">
<div slot="tip">只能上传jpg/png文件且不超过500kb</div>
</agUpload>
<ag-form :data="formdata" label-width="120px" label-position="left" v-model="formvalue">
</ag-form>
<ag-mution-checkbox :datas="checklist" @onSearch="onCheckSearch" v-model="checkvalue"> </ag-mution-checkbox>
<!-- <el-input <!-- <el-input agMultionCheckbox
size="small" size="small"
placeholder="请输入内容" placeholder="请输入内容"
v-model="value" v-model="value"
@ -57,29 +69,35 @@
</el-select> </el-select>
<el-button slot="append" icon="el-icon-search"></el-button> <el-button slot="append" icon="el-icon-search"></el-button>
</el-input> --> </el-input> -->
<ag-multion-date-picker size="small"
ref="ttt"
v-model="datevalues"
:showExtra="true"
:clearable="true"
:range="true"
:options="a_options" />
<ag-search :data="searchdatas" v-model="searchvalue" @onRest="onRest" @onSearch="onSearch"/>
</div> </div>
</template> </template>
<script> <script>
import agDialog from "../packages/agDialog/src/index.vue"; import ElementUI from 'element-ui';
import agDatePicker from "../packages/agDatePicker/src/index.vue"; import Vue from 'vue';
import agInput from "../packages/agInput/src/index.vue";
import agSelect from "../packages/agSelect/src/index.vue";
import agNumberRange from "../packages/agNumberRange/src/index.vue";
import agCascadeOptional from "../packages/agCascadeOptional/src/index.vue"; import agCascadeOptional from "../packages/agCascadeOptional/src/index.vue";
import agMultifunctionSearch from "../packages/agMultifunctionSearch/src/index.vue"; import vuedraggable from 'vuedraggable';
import agQuery from "../packages/agQuery/src/index.vue"; import agElemUI from "../packages/index.js";
Vue.use(ElementUI);
Vue.use(agElemUI);
// Vue.use(vuedraggable);
Vue.component('vuedraggable', window.vuedraggable)
export default { export default {
components: { components: {
agDialog,
agDatePicker,
agInput,
agSelect,
agNumberRange,
agCascadeOptional, agCascadeOptional,
agMultifunctionSearch, vuedraggable
agQuery
}, },
data() { data() {
var validatePass = (rule, value, callback) => { var validatePass = (rule, value, callback) => {
@ -99,15 +117,37 @@ export default {
// } // }
}; };
return { return {
colorvalue:"#ffffff",
ruleForm: { ruleForm: {
pass: [], pass: [],
}, },
columns:[
{soltName: "date", width:200,type:"expand" },
{soltName: "name", label: "姓名" },
{prop: "xinghao", label: "型号",width:120 },
{prop: "dianchi", label: "电池" ,width:500},
{prop: "pinpai", label: "品牌" ,width:200},
{prop: "address", label: "地址" ,fixed:'right',width:100}
],
tabledata:[
{date: "2016-05-02", name: "王小虎1", address: "上海市普陀区金沙江路 1518 弄"},
{date: "2016-05-03", name: "王小虎2", address: "上海市普陀区金沙江路 1518 弄"},
{date: "2016-05-04", name: "王小虎3", address: "上海市普陀区金沙江路 1518 弄"},
{date: "2016-05-05", name: "王小虎4", address: "上海市普陀区金沙江路 1518 弄"}
],
checklist:[
{label:"方法1",value:"1"},
{label:"方法2",value:"2"}
],
rules: { rules: {
pass: [{ validator: validatePass, trigger: "blur" }], pass: [{ validator: validatePass, trigger: "blur" }],
}, },
inputValue:"",
date_value:null, date_value:null,
checkvalue:[],
value: "选项1", value: "选项1",
a_value: "59584", a_value: "59584",
datevalues:["4",["2022-12-12","2022-12-13 "]],
values: ["4",""], values: ["4",""],
options: [ options: [
{ {
@ -178,7 +218,7 @@ export default {
}, },
{ {
label:"型号", label:"型号",
elem:'el-select', elem:'el-input',
name:"model", name:"model",
options: [] options: []
}, },
@ -319,9 +359,9 @@ export default {
enName: "time", enName: "time",
TimeType: "daterange", TimeType: "daterange",
clearable: true, clearable: true,
startplaceholder: "开始日期", startPlaceholder: "开始日期",
endplaceholder: "结束日期", endPlaceholder: "结束日期",
rangeseparator: "至", rangeSeparator: "至",
format: "timestamp", format: "timestamp",
placeholder: "请选择日期", placeholder: "请选择日期",
filterable: true, filterable: true,
@ -333,18 +373,55 @@ export default {
handleChange: () => {}, handleChange: () => {},
}, },
], ],
}; filelist:['https://git.aiguoai.com/assets/img/logo.svg'],
formdata:[
{label:"订单编号",span:10,type:"ag-input",enName:'order_sn',placeholder:'请输入洗碗机',size:'small',clearable:true,show:true,value:false,required:true,options:[{label:"是",value:'1'},{label:"否",value:'2'}]},
{label:"IMEI/机器编号",span:10,type:"ag-input-number",enName:'imei',labelWidth:'120px',placeholder:'请输入IMEI/机器编号',size:'small',clearable:true,max:10,min:2,step:2,show:true},
{label:"质检码",span:12,type:"ag-rate",value:'',enName:'ser_sn',placeholder:'请输入质检码',size:'small',clearable:true,show:true,options:[{value:'1',label:'112'},{value:'2',label:'2223'}]},
{label:"质检时间",span:12,type:"ag-multifunction-search",enName:'time',showAlpha:true,size:'small',colorFormat:'hsv',show:true},
],
searchdatas:[
{label:"IMEI",span:4,type:"ag-input",enName:'order_sn',placeholder:'请输入洗碗机',size:'small',clearable:true,show:true,value:false,required:true,options:[{label:"是",value:'1'},{label:"否",value:'2'}]},
{label:"订单编号",span:4,type:"ag-input-number",enName:'imei',labelWidth:'120px',placeholder:'请输入IMEI/机器编号',size:'small',clearable:true,max:10,min:2,step:2,show:true},
{label:"质检码",span:4,type:"ag-rate",value:'',enName:'ser_sn',placeholder:'请输入质检码',size:'small',clearable:true,show:true,},
{label:"质检时间",span:6,type:"ag-multion-input",enName:'time',showAlpha:true,size:'small',colorFormat:'hsv',options:[{value:'1',label:'112'},{value:'2',label:'2223'}],show:true},
],
searchvalue:{
order_sn:"11",
imei:'',
ser_sn:2,
timer:['1','2']
}, },
watch: { formvalue:{
values: { order_sn:"11",
handler(val) { imei:'',
console.log(val, 238); ser_sn:2,
}, time:['11','2'],
deep: true, avatar:["https://git.aiguoai.com/assets/img/logo.svg"],
immediate: true, }
}
}, },
mounted(){
this.$refs.aginputref.value=123;
}, },
methods: { methods: {
onUpload(files,list){
console.log("onUpload",files,list);
this.filelist=list;
},
onError(err){
console.log("onError",err);
},
onblur(val,elem){
console.log("onblur",val,elem,this);
},
onInputChange(value){
console.log(value,"onSearch");
},
onPicker(value){ onPicker(value){
this.date_value=value; this.date_value=value;
}, },
@ -352,7 +429,7 @@ export default {
console.log(values,"onSearch"); console.log(values,"onSearch");
}, },
onInputinput(value){ onInputinput(value){
console.log(value,"oninput"); console.log(value,"oninput222");
this.values=value; this.values=value;
}, },
change() { change() {
@ -361,6 +438,9 @@ export default {
submit() { submit() {
this.$refs.ttt.values; this.$refs.ttt.values;
}, },
onCheckSearch(val){
console.log(val,'onCheckSearch',this.checkvalue);
},
changes(val) { changes(val) {
console.log(val); console.log(val);
console.log(999); console.log(999);
@ -368,13 +448,16 @@ export default {
handleSizeChange() {}, handleSizeChange() {},
handleCurrentChange() {}, handleCurrentChange() {},
handleClose() {}, handleClose() {},
onRest(val){
console.log(val,"onRest")
}
}, },
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
body { body {
background: #918d8d; background: #f8f8f8;
} }
.div { .div {
span { span {

43798
src/ag-ui.common.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -5130,6 +5130,11 @@ sockjs@^0.3.24:
uuid "^8.3.2" uuid "^8.3.2"
websocket-driver "^0.7.4" websocket-driver "^0.7.4"
sortablejs@1.10.2:
version "1.10.2"
resolved "http://112.124.55.31:4873/sortablejs/-/sortablejs-1.10.2.tgz#6e40364d913f98b85a14f6678f92b5c1221f5290"
integrity sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==
"source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0: "source-map-js@>=0.6.2 <2.0.0", source-map-js@^1.2.0:
version "1.2.0" version "1.2.0"
resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af" resolved "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz#16b809c162517b5b8c3e7dcd315a2a5c2612b2af"
@ -5576,6 +5581,13 @@ vue@^2.6.14:
"@vue/compiler-sfc" "2.7.16" "@vue/compiler-sfc" "2.7.16"
csstype "^3.1.0" csstype "^3.1.0"
vuedraggable@^2.24.3:
version "2.24.3"
resolved "http://112.124.55.31:4873/vuedraggable/-/vuedraggable-2.24.3.tgz#43c93849b746a24ce503e123d5b259c701ba0d19"
integrity sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==
dependencies:
sortablejs "1.10.2"
vuex@^3.6.2: vuex@^3.6.2:
version "3.6.2" version "3.6.2"
resolved "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71" resolved "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz#236bc086a870c3ae79946f107f16de59d5895e71"