Browse Source

增加全局注册

develop
zq 3 months ago
parent
commit
e054ddf26c
  1. 169
      examples/src/App.vue
  2. 7
      packages/CustomDropdown/index.js
  3. 421
      packages/CustomDropdown/src/index.vue
  4. 49
      packages/index.js
  5. 50
      packages/styles/component.scss
  6. 8
      packages/utils/eventBus.js

169
examples/src/App.vue

@ -109,30 +109,30 @@
<section class="demo-section">
<h2>提示框</h2>
<div class="ele-item">
<GuipButton type="system" @click="openMessage('success')">成功提示</GuipButton>
<GuipButton type="system" @click="openMessage('error')"> 失败提示</GuipButton>
<GuipButton type="system" @click="openMessage('info')">警告提示</GuipButton>
<GuipButton type="system" @click="openMessage('success')">成功提示</GuipButton>
<GuipButton type="system" @click="openMessage('error')"> 失败提示</GuipButton>
<GuipButton type="system" @click="openMessage('info')">警告提示</GuipButton>
</div>
</section>
<section class="demo-section">
<h2>复制功能</h2>
<div class="ele-item">
<label for="">copy固定内容</label>
<!-- 复制固定文本 -->
<GuipButton size="big" v-clipboard="'渝过田晴'">复制渝过田晴</GuipButton>
<!-- 复制动态文本 -->
<GuipButton size="big" v-clipboard="content">点击复制: {{ content }}</GuipButton>
<label for="">copy固定内容</label>
<!-- 复制固定文本 -->
<GuipButton size="big" v-clipboard="'渝过田晴'">复制渝过田晴</GuipButton>
<!-- 复制动态文本 -->
<GuipButton size="big" v-clipboard="content">点击复制: {{ content }}</GuipButton>
</div>
<div class="ele-item">
<label for="">手动点击copy</label>
<GuipInput v-model="form.input1">
<!-- 提示可以不添加 图标可更换 -->
<GuipToolTip content="点击复制到粘贴板" slot="suffix">
<img src="../assets/home-bread.svg" @click="handleClickCopy" />
</GuipToolTip>
</GuipInput>
<label for="">手动点击copy</label>
<GuipInput v-model="form.input1">
<!-- 提示可以不添加 图标可更换 -->
<GuipToolTip content="点击复制到粘贴板" slot="suffix">
<img src="../assets/home-bread.svg" @click="handleClickCopy" />
</GuipToolTip>
</GuipInput>
</div>
</section>
<section class="demo-section">
@ -184,18 +184,18 @@
<GuipSelect width="600px" v-model="form.card" clearable label="卡片" :default-value="form.card"
@change="selectChangeTest" prop="card" :options="options" valueKey="id1" labelKey="id2"
:extraItem="{ label: '全部', value: '99999' }" />
</div>
</section>
<section class="demo-section">
<h2>自定义表单展示形式</h2>
<!-- 必须添加 slot -->
<div style="width: 400px;">
<GuipFormItem column="column" class="mb24" label="自定义左侧">
<span class="desc" slot="formRight"><a href="https://www.baidu.com/">跳转一下</a> 自定义右侧</span>
<GuipInput slot="formDom" ref="GuipInput" v-model="form.input1" width="100%" placeholder="这是自定义默认提示语" />
</GuipFormItem>
</div>
</div>
</section>
<section class="demo-section">
<h2>自定义表单展示形式</h2>
<!-- 必须添加 slot -->
<div style="width: 400px;">
<GuipFormItem column="column" class="mb24" label="自定义左侧">
<span class="desc" slot="formRight"><a href="https://www.baidu.com/">跳转一下</a> 自定义右侧</span>
<GuipInput slot="formDom" ref="GuipInput" v-model="form.input1" width="100%" placeholder="这是自定义默认提示语" />
</GuipFormItem>
</div>
</section>
<section class="demo-section">
@ -308,6 +308,103 @@
</div>
</section>
<div class="flex ele-item">
<label for="">inputdrop组合使用(默认使用)</label>
<GuipFormItem column="column" class="combo-formItem w510" label="域名设置" required="true">
<div slot="formDom" class="self-drop-wrap flex">
<GuipInput style="width: 60%;" v-model="form.domain_set" placeholder="仅支持数字、字母">
</GuipInput>
<!-- 只用作选中内容展示 -->
<div @click="toggleDrop" class="point flex appendDrop" style="width: 40%;">
{{ currentDomainItem.name }}</div>
</div>
<!--触发 真实下拉操作 -->
<!-- valueKey="id" displayKey="name" 不添加时默认取值 valuelabel-->
<CustomDropdown slot="formDom" ref="dropDomain" width="100%" v-model="form.domainSuffix1"
group="account-selector" :options="domainOptions" @change="changeSelectIp" placeholder="请选择">
<template #normal>
<div class="flex flex-between noraml-jump">
<div class="left">
<b>添加新域名</b>
<p class="one ft12">域名需要在阿里云完成ICP备案并解析到平台服务器</p>
<p class="ft12">如果暂时未准备好可先选用平台免费域名随时支持域名修改 </p>
</div>
<div class="right">
<GuipButton size="form">前往绑定</GuipButton>
</div>
</div>
</template>
</CustomDropdown>
</GuipFormItem>
</div>
<div class="flex ele-item">
<label for="">inputdrop组合使用(自定义下拉选择项)</label>
<GuipFormItem column="column" class="combo-formItem w510" label="域名设置" required="true">
<div slot="formDom" class="self-drop-wrap flex">
<GuipInput style="width: 60%;" v-model="form.domain_set" placeholder="仅支持数字、字母">
</GuipInput>
<!-- 只用作选中内容展示 -->
<div @click="toggleDrop1" class="point flex appendDrop" style="width: 40%;">
{{ currentDomainItem.name }}
</div>
</div>
<!--触发 真实下拉操作 -->
<!-- valueKey="id" displayKey="name" 不添加时默认取值 valuelabel-->
<CustomDropdown slot="formDom" ref="dropDomain1" width="100%" v-model="form.domainSuffix"
group="account-selector" :options="domainOptions" @change="changeSelectIp" valueKey="id" placeholder="请选择">
<template #normal>
<div class="flex flex-between noraml-jump">
<div class="left">
<b>添加新域名</b>
<p class="one ft12">域名需要在阿里云完成ICP备案并解析到平台服务器</p>
<p class="ft12">如果暂时未准备好可先选用平台免费域名随时支持域名修改 </p>
</div>
<div class="right">
<GuipButton size="form">前往绑定</GuipButton>
</div>
</div>
</template>
<!-- 自定义下拉选项 选中条件-->
<!-- 不添加这个 默认普通选中格式 单独取值 display-key
自定义渲染项 渲染的内容自定义 display-key 属性不再生效-->
<template #item="{ item }">
<div class="flex-between">
<div class="left">
<p class="one">{{ item.label }}</p>
</div>
<div class="right">
<span v-if="form.domainSuffix == item.id">*</span>
</div>
</div>
</template>
</CustomDropdown>
</GuipFormItem>
</div>
<div class="flex ele-item">
<label for="">单独实现自定义内容下拉选择</label>
<CustomDropdown width="500px" v-model="form.domainSuffix" :options="domainOptions" group="account-selector"
@change="changeSelectIp" valueKey="id" placeholder="请选择">
<template #normal>
<div class="flex flex-between noraml-jump">
<div class="left">
<b>添加新域名</b>
<p class="one ft12">域名需要在阿里云完成ICP备案并解析到平台服务器</p>
<p class="ft12">如果暂时未准备好可先选用平台免费域名随时支持域名修改 </p>
</div>
<div class="right">
<GuipButton size="form">前往绑定</GuipButton>
</div>
</div>
</template>
<template #item="{ item }">
<div class="flex-between">
<p>测试一下自定义内容{{ item.id }} + {{ item.label }}</p>
<p>易烊千玺/田栩宁</p>
</div>
</template>
</CustomDropdown>
</div>
<GuipDialog :dialogVisible="dialogVisible" title="自定义标题" :show-close-button="false"
:show-cancel-button="showCancelButton" @confirm="handleConfirm" @cancel="handleCancel" @close="handleClose"
@dialogVisibleChange="dialogVisibleChange">
@ -339,6 +436,8 @@
</div>
</GuipDialog>
<div style="width: 100%;height: 500px;"></div>
</el-form>
@ -705,11 +804,13 @@ export default {
// showClose: true,
// center: true
// })
this.$Message({
message: '这是一条普通消息',
type: 'info',
duration: 3000
})
// =----ok
// this.$Message({
// message: '',
// type: 'info',
// duration: 3000
// })
},
computed: {
currentDomainItem() {
@ -729,7 +830,7 @@ export default {
},
// copy
handleClickCopy() {
// GuipMessage.success('')
// this.$Message.success('')
this.$copy(this.form.input1, {

7
packages/CustomDropdown/index.js

@ -0,0 +1,7 @@
import CustomDropdown from './src/index.vue'
CustomDropdown.install = function(Vue) {
Vue.component(CustomDropdown.name || 'CustomDropdown', CustomDropdown)
}
export default CustomDropdown

421
packages/CustomDropdown/src/index.vue

@ -0,0 +1,421 @@
<template>
<div class="custom-select" v-clickaway="handleClickAway" ref="dropdown" :class="{ 'is-open': state.isOpen }"
:style="{ width: computedWidth }">
<!-- 触发按钮 -->
<div class="select-trigger" @click.stop="toggleDropdown">
<slot name="trigger">
{{ displayText }}
</slot>
<img class="arrow-icon" :src="state.isOpen ? openIcon : expandIcon" alt="dropdown indicator">
</div>
<!-- 下拉内容 -->
<transition name="slide-fade">
<div v-show="state.isOpen" class="select-dropdown">
<slot v-if="state.isOpen" name="normal"></slot>
<template v-if="filteredOptions.length">
<div v-for="(item, index) in filteredOptions" :key="`option-${index}`" class="dropdown-item"
:class="{ 'is-selected': isSelected(item) }" @click.stop="selectItem(item)">
<slot name="item" :item="item">
<div class="flex-between">
<div class="left">
<p class="one">{{ item[displayKey] }}</p>
</div>
<div class="right">
<img v-if="isSelected(item)" :src="selectedIcon" alt="selected">
</div>
</div>
</slot>
</div>
</template>
<div v-if="showNullOption" class="flex-between dropdown-item" @click.stop="selectNullItem">
<div class="left">
<p class="one">暂无收款账号</p>
<p>暂时没有收款账号我想稍后配置</p>
</div>
<div class="right">
<img :src="selectedIcon" alt="selected">
</div>
</div>
</div>
</transition>
</div>
</template>
<script>
// Webpack
const STATIC_ICONS = Object.freeze({
expand: require('../../assets/dropDown_expand.png'),
open: require('../../assets/dropDown_open.png'),
selected: require('../../assets/drop-selected.svg')
})
export default {
name: 'CustomDropdown',
props: {
width: {
type: String,
default: "200px",
validator: (val) => /^\d+(px|%|rem|em|vw)$/.test(val)
},
options: {
type: Array,
default: () => [],
validator: (arr) => !arr.some(item => item === null || typeof item !== 'object')
},
nullOption: {
type: Object,
default: null
},
placeholder: {
type: String,
default: "请选择"
},
value: {
type: [String, Number, Object],
default: null
},
valueKey: {
type: String,
default: "value",
validator: (key) => typeof key === 'string' && key.trim().length > 0
},
displayKey: {
type: String,
default: "label",
validator: (key) => typeof key === 'string' && key.trim().length > 0
},
group: {
type: String,
default: null
},
},
// 使
data: () => ({
state: Object.seal({
isOpen: false,
lastEmittedValue: null,
updateDepth: 0,
eventLock: false
}),
icons: STATIC_ICONS
}),
computed: {
computedWidth() {
// console.log(this.width.endsWith('px'),this.width,'this.width.endsWith');
// return this.width.endsWith('px') ? this.width : `${parseInt(this.width)}px`
return this.width
},
filteredOptions() {
return Array.isArray(this.options) ? [...this.options] : []
},
showNullOption() {
return this.nullOption && !this.filteredOptions.length
},
displayText() {
const current = this.findOptionByValue(this.value)
return current ? current[this.displayKey] : this.placeholder
},
openIcon() {
return this.icons.open
},
expandIcon() {
return this.icons.expand
},
selectedIcon() {
return this.icons.selected
}
},
created() {
//
if (this.group) {
this.$busOn(`dropdown-group-${this.group}`, (openedId) => {
if (openedId !== this._uid && this.state.isOpen) {
this.closeDropdown();
}
});
}
// console.log(`Dropdown created with _uid:`, this._uid);
},
watch: {
value: {
immediate: true,
handler(newVal) {
if (this.state.eventLock) return
this.state.lastEmittedValue = JSON.stringify(newVal)
}
}
},
methods: {
//
findOptionByValue(value) {
if (value === null || value === undefined) return null
return this.filteredOptions.find(item => {
return JSON.stringify(item[this.valueKey]) === JSON.stringify(value)
})
},
isSelected(item) {
if (!this.value) return false
return JSON.stringify(item[this.valueKey]) === JSON.stringify(this.value)
},
//
// handleClickAway() {
// // console.log(this.state.isOpen,'1111----');
// // if (!this.state.isOpen) return
// // console.log(this.state.isOpen,'2222----');
// // this.state.isOpen = false
// // console.log(this.state.isOpen,'333----');
// // this.$nextTick(() => {
// // this.$busEmit('closed')
// // })
// //
// if (this.$refs.dropdown.contains(event.target)) return;
// if (this.state.isOpen) {
// this.state.isOpen = false;
// this.$busEmit('closed');
// }
// },
toggleDropdown() {
console.log('Toggling dropdown, current state:', this.state.isOpen)
if (this.state.eventLock) return
//
// this.state.isOpen = !this.state.isOpen
//
console.log('New state:', this.state.isOpen)
console.log('Dropdown element:', this.$refs.dropdown)
//
// if (this.state.isOpen) {
// // this.$busEmit('opened')
// this.openDropdown();
// } else {
// this.closeDropdown();
// this.$busEmit('closed')
// }
if (this.state.isOpen) {
this.closeDropdown();
} else {
this.openDropdown();
}
//
this.$forceUpdate()
},
//
closeDropdown() {
this.state.isOpen = false;
this.$busEmit('closed');
this.$emit('closed');
},
//
openDropdown() {
//
if (this.group) {
this.$busEmit(`dropdown-group-${this.group}`, this._uid);
}
this.state.isOpen = true;
this.$busEmit('opened');
this.$emit('opened');
},
//
// toggleDropdown() {
// if (this.state.eventLock) return;
// if (this.state.isOpen) {
// this.closeDropdown();
// } else {
// this.openDropdown();
// }
// },
//
handleClickAway(event) {
//
// debugger
// const trigger = this.$el.querySelector('.select-trigger');
// if (trigger && trigger.contains(event.target)) return;
// if (this.state.isOpen) {
// this.closeDropdown();
// }
console.log('Before:', {
isOpen: this.state.isOpen,
display: this.$refs.dropdown.querySelector('.select-dropdown').style.display
});
//
const dropdownEl = this.$refs.dropdown.querySelector('.select-dropdown');
const isActuallyVisible = dropdownEl &&
(dropdownEl.style.display !== 'none');
if (isActuallyVisible && !this.state.isOpen) {
//
this.state.isOpen = true;
this.$nextTick(() => {
this.closeDropdown();
});
return;
}
//
if (this.state.isOpen) {
this.closeDropdown();
}
},
//
selectItem(item) {
if (this.state.updateDepth > 1) {
console.error('Recursion detected in dropdown selection')
this.state.isOpen = false
this.state.updateDepth = 0
return
}
const newValue = item[this.valueKey]
//
if (this.state.lastEmittedValue === JSON.stringify(newValue)) {
this.state.isOpen = false
return
}
this.state.updateDepth++
this.state.eventLock = true
this.state.lastEmittedValue = JSON.stringify(newValue)
// 使setTimeout
setTimeout(() => {
try {
this.$emit('input', newValue)
this.$emit('change', Object.freeze({ ...item }))
} catch (e) {
console.error('Dropdown emit error:', e)
} finally {
this.state.isOpen = false
this.state.updateDepth = 0
this.state.eventLock = false
}
}, 0)
},
selectNullItem() {
this.state.eventLock = true
this.state.lastEmittedValue = null
setTimeout(() => {
this.$emit('input', null)
this.$emit('change', null)
this.state.isOpen = false
this.state.eventLock = false
}, 0)
}
},
beforeDestroy() {
if (this.group) {
this.$busOff(`dropdown-group-${this.group}`);
}
// performance.measure('dropdown-lifetime', this.$_performanceMark)
}
}
</script>
<style scoped lang="scss">
/* Your existing styles remain the same */
.custom-select {
display: inline-block;
vertical-align: middle;
height: 38px;
position: relative;
font-family: Arial, sans-serif;
}
.select-trigger {
border-radius: 2px;
opacity: 1;
background: #FFFFFF;
border: 1px solid #DFE2E6;
width: 100%;
height: 40px;
box-sizing: border-box;
padding: 10px 12px;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.is-open .select-trigger {
border: 1px solid #006AFF;
transition: all .5s;
outline: 3px solid #D8E9FA;
}
.select-trigger:hover {
border-color: #006AFF;
transition: all .5s;
}
.arrow-icon {
width: 12px;
}
.select-dropdown {
position: absolute;
top: 100%;
right: 0;
width: 100%;
border: 1px solid #ccc;
box-sizing: border-box;
border-radius: 4px;
background-color: #fff;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
z-index: 1000;
margin-top: 4px;
max-height: 384px;
overflow-y: auto;
padding: 0 12px 12px 12px;
}
.dropdown-item {
padding: 12px 10px;
cursor: pointer;
}
.dropdown-item:hover {
background: #F6F7FA;
}
.dropdown-item.is-selected {
background-color: #F6F7FA;
color: #006AFF;
}
/* 展开收起动画 */
.slide-fade-enter-active,
.slide-fade-leave-active {
transition: all 0.3s ease;
}
.slide-fade-enter-from,
.slide-fade-leave-to {
opacity: 0;
transform: translateY(-10px);
}
</style>

49
packages/index.js

@ -14,10 +14,13 @@ import GuipSwitch from './GuipSwitch';
import GuipTooltip from './GuipToolTip';
import GuipDialog from './GuipDialog';
import GuipMessage from './GuipMessage';
// import CustomDropdown from './CustomDropdown';
import GuipFormItem from './GuipFormItem';
import { directive as clickaway } from 'vue-clickaway';
import {
directive as clickaway
} from 'vue-clickaway';
// 表格头部 添加自定义小图标 *
@ -27,7 +30,9 @@ import { directive as clickaway } from 'vue-clickaway';
// 复制到粘贴板
import clipboard from './utils/dirClipBoard';
import { modernCopyToClipboard } from './utils/clipboard';
import {
modernCopyToClipboard
} from './utils/clipboard';
import 'core-js/stable';
import 'element-ui/lib/theme-chalk/index.css'; // 如果依赖Element
@ -35,12 +40,12 @@ import './styles/index.css'; // 全局引入
import './styles/common.scss'; // 全局引入
import './styles/component.scss'; // 全局引入
// import EventBus, {
// $on,
// $once,
// $off,
// $emit
// } from './utils/eventBus'
import EventBus, {
$busOn,
$busOnce,
$busOff,
$busEmit
} from './utils/eventBus'
const components = [
GuipButton,
@ -59,18 +64,20 @@ const components = [
GuipSwitch,
GuipDialog,
GuipFormItem,
]
// CustomDropdown
];
const install = function (Vue,options={}) {
// 挂载到Vue原型
// Vue.prototype.$eventBus = EventBus
// Vue.prototype.$on = $on
// Vue.prototype.$emit = $emit
// Vue.prototype.$off = $off
// Vue.prototype.$once = $once
const install = function (Vue, options = {}) {
// 为了避免冲突 名字要和特殊api 区分
// 挂载到Vue原型
Vue.prototype.$eventBus = EventBus
Vue.prototype.$busOn = $busOn
Vue.prototype.$busEmit = $busEmit
Vue.prototype.$busOff = $busOff
Vue.prototype.$busOnce = $busOnce
Vue.prototype.$Message = GuipMessage
// 复制
Vue.prototype.$copy = modernCopyToClipboard;
Vue.use(clipboard);
@ -84,7 +91,7 @@ const install = function (Vue,options={}) {
})
}
export {
export {
GuipButton,
GroupFormBtns,
GuipInput,
@ -101,7 +108,8 @@ export {
GuipSwitch,
GuipDialog,
GuipFormItem,
GuipMessage
GuipMessage,
// CustomDropdown
}
export default {
install,
@ -121,5 +129,6 @@ export default {
GuipSwitch,
GuipDialog,
GuipFormItem,
GuipMessage
GuipMessage,
// CustomDropdown
}

50
packages/styles/component.scss

@ -488,4 +488,52 @@
transform: translateY(20px);
}
// 提示信息组件 end
// 提示信息组件 end
// 自定义下拉框input+下拉组合框
.combo-formItem {
::v-deep {
.form-item-bottom {
position: relative;
}
.select-trigger {
background: #F6F7FA;
border-color: transparent;
}
.is-open .select-trigger {
border-color: #006AFF;
}
.el-input__inner {
border-radius: 2px 0 0 2px;
}
}
.self-drop-wrap {
position: absolute;
z-index: 1;
width: 100%;
}
.appendDrop {
height: 38px;
align-items: center;
border-radius: 0 2px 2px 0;
border: 1px solid #DFE2E6;
border-left-color: transparent;
justify-content: center;
box-sizing: border-box;
padding: 0 30px 0 12px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
&:hover {
border: 1px solid #006AFF;
}
}
}
// 组合框样式 end

8
packages/utils/eventBus.js

@ -3,9 +3,9 @@ import Vue from 'vue'
const EventBus = new Vue()
// 封装常用方法
export const $on = EventBus.$on.bind(EventBus)
export const $once = EventBus.$once.bind(EventBus)
export const $off = EventBus.$off.bind(EventBus)
export const $emit = EventBus.$emit.bind(EventBus)
export const $busOn = EventBus.$on.bind(EventBus)
export const $busOnce = EventBus.$once.bind(EventBus)
export const $busOff = EventBus.$off.bind(EventBus)
export const $busEmit = EventBus.$emit.bind(EventBus)
export default EventBus
Loading…
Cancel
Save