feat: 部分组件重构完成
parent
0f5c55c36d
commit
3d9622978d
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
ref="dialogRef"
|
||||
v-bind="getBindValue"
|
||||
:fullscreen="fullscreen"
|
||||
destroy-on-close
|
||||
lock-scroll
|
||||
:close-on-click-modal="false"
|
||||
top="10vh"
|
||||
>
|
||||
<template #title>
|
||||
<slot name="title">
|
||||
{{ title }}
|
||||
</slot>
|
||||
<svg-icon
|
||||
v-if="showFullscreen"
|
||||
:icon-class="fullscreen ? 'exit-fullscreen' : 'fullscreen'"
|
||||
class-name="dialog__icon"
|
||||
@click="toggleFull"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- 弹窗内容 -->
|
||||
<el-scrollbar
|
||||
:class="
|
||||
fullscreen && slots.footer
|
||||
? 'com-dialog__content--footer'
|
||||
: fullscreen && !slots.footer
|
||||
? 'com-dialog__content--fullscreen'
|
||||
: 'com-dialog__content'
|
||||
"
|
||||
>
|
||||
<div class="content__wrap">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
<template v-if="slots.footer" #footer>
|
||||
<slot name="footer"></slot>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="Dialog">
|
||||
import { ref, computed, PropType, nextTick, unref, useAttrs, useSlots } from 'vue'
|
||||
import SvgIcon from '@/components/SvgIcon/index.vue'
|
||||
|
||||
const slots = useSlots()
|
||||
|
||||
const props = defineProps({
|
||||
title: {
|
||||
type: String as PropType<string>,
|
||||
default: ''
|
||||
},
|
||||
// 是否显示全屏按钮
|
||||
showFullscreen: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true
|
||||
},
|
||||
// 是否可以拖拽
|
||||
draggable: {
|
||||
type: Boolean as PropType<boolean>,
|
||||
default: true
|
||||
}
|
||||
})
|
||||
const dialogRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const fullscreen = ref<boolean>(false)
|
||||
|
||||
const getBindValue = computed((): any => {
|
||||
const delArr: string[] = ['showFullscreen', 'draggable']
|
||||
const attrs = useAttrs()
|
||||
const obj = { ...attrs, ...props }
|
||||
for (const key in obj) {
|
||||
if (delArr.indexOf(key) !== -1) {
|
||||
delete obj[key]
|
||||
}
|
||||
}
|
||||
return obj
|
||||
})
|
||||
|
||||
function toggleFull(): void {
|
||||
fullscreen.value = !fullscreen.value
|
||||
// 全屏的时候需要重新定义left top
|
||||
if (fullscreen.value && props.draggable) {
|
||||
const dragDom = unref(dialogRef as any).$refs.dialogRef
|
||||
dragDom.style.cssText += `;left:0px;top:0px;`
|
||||
}
|
||||
}
|
||||
|
||||
function initDraggable() {
|
||||
nextTick(() => {
|
||||
const dragDom = unref(dialogRef as any).$refs.dialogRef
|
||||
const dialogHeaderEl = dragDom.querySelector('.el-dialog__header') as HTMLElement
|
||||
dragDom.style.cssText += ';top:0px;'
|
||||
dialogHeaderEl.style.cssText += ';cursor:move;user-select:none;'
|
||||
dialogHeaderEl.onmousedown = (e) => {
|
||||
const disX = e.clientX - dialogHeaderEl.offsetLeft
|
||||
const disY = e.clientY - dialogHeaderEl.offsetTop
|
||||
|
||||
const dragDomWidth = dragDom.offsetWidth
|
||||
const dragDomHeight = dragDom.offsetHeight
|
||||
|
||||
const screenWidth = document.body.clientWidth
|
||||
const screenHeight = document.body.clientHeight
|
||||
|
||||
const minDragDomLeft = dragDom.offsetLeft
|
||||
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth
|
||||
|
||||
const minDragDomTop = dragDom.offsetTop
|
||||
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomHeight
|
||||
|
||||
const styleLeftStr = getComputedStyle(dragDom).left
|
||||
const styleTopStr = getComputedStyle(dragDom).top
|
||||
if (!styleLeftStr || !styleTopStr) return
|
||||
let styleLeft: number
|
||||
let styleTop: number
|
||||
|
||||
// Format may be "##%" or "##px"
|
||||
if (styleLeftStr.includes('%')) {
|
||||
styleLeft = +document.body.clientWidth * (+styleLeftStr.replace(/%/g, '') / 100)
|
||||
styleTop = +document.body.clientHeight * (+styleTopStr.replace(/%/g, '') / 100)
|
||||
} else {
|
||||
styleLeft = +styleLeftStr.replace(/px/g, '')
|
||||
styleTop = +styleTopStr.replace(/px/g, '')
|
||||
}
|
||||
|
||||
document.onmousemove = (e) => {
|
||||
let left = e.clientX - disX
|
||||
let top = e.clientY - disY
|
||||
|
||||
// Handle edge cases
|
||||
if (-left > minDragDomLeft) {
|
||||
left = -minDragDomLeft
|
||||
} else if (left > maxDragDomLeft) {
|
||||
left = maxDragDomLeft
|
||||
}
|
||||
if (-top > minDragDomTop) {
|
||||
top = -minDragDomTop
|
||||
} else if (top > maxDragDomTop) {
|
||||
top = maxDragDomTop
|
||||
}
|
||||
|
||||
// Move current element
|
||||
dragDom.style.cssText += `;left:${left + styleLeft}px;top:${top + styleTop}px;`
|
||||
}
|
||||
|
||||
document.onmouseup = () => {
|
||||
document.onmousemove = null
|
||||
document.onmouseup = null
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (props.draggable) {
|
||||
initDraggable()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.dialog__icon {
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
right: 45px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
&:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
}
|
||||
.com-dialog__content {
|
||||
.content__wrap {
|
||||
padding-right: 10px;
|
||||
}
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
max-height: 600px; // 最大高度
|
||||
overflow-x: hidden; // 隐藏横向滚动栏
|
||||
}
|
||||
}
|
||||
.com-dialog__content--fullscreen {
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
height: calc(~'100vh - 46px - 60px'); // 最大高度
|
||||
}
|
||||
}
|
||||
.com-dialog__content--footer {
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
max-height: calc(~'100vh - 46px - 60px - 70px'); // 最大高度
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,13 @@
|
||||
export interface EditorConfig {
|
||||
height?: number // 富文本高度
|
||||
zIndex?: number // 层级
|
||||
placeholder?: string // 提示文字
|
||||
focus?: boolean // 是否聚焦
|
||||
onchangeTimeout?: number // 几秒监听一次变化
|
||||
customAlert?: (s: string, t: string) => {} // 自定义提示
|
||||
menus?: string[] // 按钮菜单
|
||||
colors?: string[] // 颜色
|
||||
fontNames?: string[] // 字体
|
||||
lineHeights?: string[] // 行间距
|
||||
showFullScreen?: boolean // 是否全屏
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
export interface LogoTypes {
|
||||
src?: string
|
||||
logoSize?: number
|
||||
bgColor?: string
|
||||
borderSize?: number
|
||||
crossOrigin?: string
|
||||
borderRadius?: number
|
||||
logoRadius?: number
|
||||
}
|
||||
@ -1,6 +1,10 @@
|
||||
import type { App } from 'vue'
|
||||
import SvgIcon from './SvgIcon/index.vue' // svg组件
|
||||
import ComSearch from './Search/index.vue' // search组件
|
||||
import ComDialog from './Dialog/index.vue' // dialog组件
|
||||
|
||||
export function setupGlobCom(app: App<Element>): void {
|
||||
app.component('SvgIcon', SvgIcon)
|
||||
app.component('ComSearch', ComSearch)
|
||||
app.component('ComDialog', ComDialog)
|
||||
}
|
||||
|
||||
@ -0,0 +1,71 @@
|
||||
import Clipboard from 'clipboard'
|
||||
import { Directive, DirectiveBinding } from 'vue'
|
||||
import { Message } from '_c/Message'
|
||||
|
||||
if (!Clipboard) {
|
||||
throw new Error('you should npm install `clipboard` --save at first ')
|
||||
}
|
||||
|
||||
export const clipboard: Directive = {
|
||||
beforeMount(el: HTMLElement, binding: DirectiveBinding) {
|
||||
createdClipboard(el, binding.arg, binding.value)
|
||||
},
|
||||
updated(el: HTMLElement | any, binding: DirectiveBinding) {
|
||||
if (binding.arg === 'success') {
|
||||
el._v_clipboard_success = binding.value
|
||||
} else if (binding.arg === 'error') {
|
||||
el._v_clipboard_error = binding.value
|
||||
} else {
|
||||
el._v_clipboard.text = function () {
|
||||
return binding.value
|
||||
}
|
||||
el._v_clipboard.action = function () {
|
||||
return 'copy'
|
||||
}
|
||||
}
|
||||
},
|
||||
unmounted(el: HTMLElement | any, binding: DirectiveBinding) {
|
||||
if (binding.arg === 'success') {
|
||||
delete el._v_clipboard_success
|
||||
} else if (binding.arg === 'error') {
|
||||
delete el._v_clipboard_error
|
||||
} else {
|
||||
el._v_clipboard.destroy()
|
||||
delete el._v_clipboard
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createdClipboard(el: HTMLElement | any, arg: string | undefined, value: any) {
|
||||
if (arg === 'success') {
|
||||
el._v_clipboard_success = value
|
||||
} else if (arg === 'error') {
|
||||
el._v_clipboard_error = value
|
||||
} else {
|
||||
const clipboard = new Clipboard(el, {
|
||||
text() {
|
||||
return value
|
||||
},
|
||||
action() {
|
||||
return 'copy'
|
||||
}
|
||||
})
|
||||
clipboard.on('success', (e) => {
|
||||
const callback = el._v_clipboard_success
|
||||
if (callback) {
|
||||
callback(e)
|
||||
} else {
|
||||
Message.success('复制成功')
|
||||
}
|
||||
})
|
||||
clipboard.on('error', (e) => {
|
||||
const callback = el._v_clipboard_error
|
||||
if (callback) {
|
||||
callback(e)
|
||||
} else {
|
||||
Message.success('复制失败')
|
||||
}
|
||||
})
|
||||
el._v_clipboard = clipboard
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
import type { App } from 'vue'
|
||||
|
||||
import { clipboard } from './clipboard'
|
||||
|
||||
export function setupDirectives(app: App) {
|
||||
app.directive('clipboard', clipboard)
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
/**
|
||||
* element配置
|
||||
*/
|
||||
export interface ConfigElement {
|
||||
zIndex: number
|
||||
size: 'medium' | 'small' | 'mini'
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="对 Element 的 Dialog 组件进行二次封装,支持所有原生参数。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-button type="primary" @click="visible = true">打开弹窗</el-button>
|
||||
|
||||
<com-dialog v-model="visible" title="提示">
|
||||
<div style="height: 1000px"> 我是弹窗内容 </div>
|
||||
<template #footer>
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="visible = false">确定</el-button>
|
||||
</template>
|
||||
</com-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="DialogDemo">
|
||||
import { ref } from 'vue'
|
||||
const visible = ref<boolean>(false)
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@ -0,0 +1,45 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="基于 wangeditor 封装的 富文本 组件。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
|
||||
<editor ref="editorRef" :value="content" @change="handleChange" />
|
||||
|
||||
<div style="margin-top: 20px; text-align: center">
|
||||
<el-button @click="showHtml"> 获取TTML(请在控制台查看) </el-button>
|
||||
<el-button @click="showText"> 获取TEXT(请在控制台查看) </el-button>
|
||||
<el-button @click="showJson"> 获取JSON(请在控制台查看) </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="EditorDemo">
|
||||
import { ref, unref } from 'vue'
|
||||
import Editor from '_c/Editor/index.vue'
|
||||
|
||||
const content = ref<string>('默认展示数据')
|
||||
const editorRef = ref<Nullable<any>>(null)
|
||||
|
||||
function handleChange(html: string) {
|
||||
console.log(html)
|
||||
}
|
||||
|
||||
function showHtml() {
|
||||
console.log((unref(editorRef) as any).getHtml())
|
||||
}
|
||||
|
||||
function showText() {
|
||||
console.log((unref(editorRef) as any).getText())
|
||||
}
|
||||
|
||||
function showJson() {
|
||||
console.log((unref(editorRef) as any).getJSON())
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@ -0,0 +1,24 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-alert
|
||||
effect="dark"
|
||||
:closable="false"
|
||||
title="二次封装 Element 的 Message 组件,每次只显示最新一条消息,避免出现太多消息提示导致不美观。"
|
||||
type="info"
|
||||
style="margin-bottom: 20px"
|
||||
/>
|
||||
<el-button @click="show">显示</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="MessageDemo">
|
||||
import { ref } from 'vue'
|
||||
import { Message } from '_c/Message'
|
||||
const count = ref<number>(0)
|
||||
function show() {
|
||||
count.value = count.value + 1
|
||||
Message.success('这是成功消息' + count.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
@ -0,0 +1,149 @@
|
||||
export const classicData = [
|
||||
{
|
||||
label: '即时配送',
|
||||
value: true,
|
||||
itemType: 'switch',
|
||||
field: 'delivery'
|
||||
},
|
||||
{
|
||||
label: '活动名称',
|
||||
value: '',
|
||||
itemType: 'input',
|
||||
field: 'name',
|
||||
placeholder: '活动名称',
|
||||
clearable: true,
|
||||
rules: [
|
||||
{
|
||||
required: true,
|
||||
message: '请输入活动名称'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '活动区域',
|
||||
value: '',
|
||||
itemType: 'select',
|
||||
placeholder: '活动区域',
|
||||
clearable: true,
|
||||
field: 'region',
|
||||
options: [
|
||||
{
|
||||
title: '区域一',
|
||||
value: 'fujian'
|
||||
},
|
||||
{
|
||||
title: '区域二',
|
||||
value: 'beijing'
|
||||
}
|
||||
],
|
||||
rules: [
|
||||
{
|
||||
itemType: 'string',
|
||||
required: true,
|
||||
message: '请选择活动区域'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: '特殊资源',
|
||||
value: '2',
|
||||
itemType: 'radio',
|
||||
field: 'resource',
|
||||
radioType: 'button', // button or radio
|
||||
options: [
|
||||
{
|
||||
label: '线上品牌商赞助',
|
||||
value: '1'
|
||||
},
|
||||
{
|
||||
label: '线下场地免费',
|
||||
value: '2'
|
||||
}
|
||||
]
|
||||
},
|
||||
// {
|
||||
// label: '组织机构',
|
||||
// value: [],
|
||||
// itemType: 'treeSelect',
|
||||
// field: 'company',
|
||||
// allowClear: true,
|
||||
// placeholder: '请选择组织机构',
|
||||
// treeCheckable: false,
|
||||
// maxTagCount: 2,
|
||||
// options: [
|
||||
// {
|
||||
// title: 'Node1',
|
||||
// value: '0-0',
|
||||
// key: '0-0',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Child Node1',
|
||||
// value: '0-0-0',
|
||||
// key: '0-0-0'
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// title: 'Node2',
|
||||
// value: '0-1',
|
||||
// key: '0-1',
|
||||
// children: [
|
||||
// {
|
||||
// title: 'Child Node3',
|
||||
// value: '0-1-0',
|
||||
// key: '0-1-0',
|
||||
// disabled: true
|
||||
// },
|
||||
// {
|
||||
// title: 'Child Node4',
|
||||
// value: '0-1-1',
|
||||
// key: '0-1-1'
|
||||
// },
|
||||
// {
|
||||
// title: 'Child Node5',
|
||||
// value: '0-1-2',
|
||||
// key: '0-1-2'
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
label: '日选择器',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date1',
|
||||
clearable: true,
|
||||
format: 'YYYY-MM-DD',
|
||||
placeholder: '请选择日期'
|
||||
},
|
||||
{
|
||||
label: '月选择器',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date2',
|
||||
clearable: true,
|
||||
format: 'YYYY-MM',
|
||||
placeholder: '请选择日期'
|
||||
},
|
||||
{
|
||||
label: '范围选择器',
|
||||
value: [],
|
||||
itemType: 'datePicker',
|
||||
field: 'date3',
|
||||
clearable: true,
|
||||
type: 'daterange',
|
||||
rangeSeparator: '至',
|
||||
startPlaceholder: '开始日期',
|
||||
endPlaceholder: '结束日期'
|
||||
},
|
||||
{
|
||||
label: '周选择器',
|
||||
value: '',
|
||||
itemType: 'datePicker',
|
||||
field: 'date4',
|
||||
type: 'week',
|
||||
clearable: true,
|
||||
placeholder: '请选择日期'
|
||||
}
|
||||
]
|
||||
Loading…
Reference in New Issue