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 type { App } from 'vue'
|
||||||
import SvgIcon from './SvgIcon/index.vue' // svg组件
|
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 {
|
export function setupGlobCom(app: App<Element>): void {
|
||||||
app.component('SvgIcon', SvgIcon)
|
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