feat: 部分组件重构完成

This commit is contained in:
kailong321200875
2021-10-17 17:01:42 +08:00
parent 0f5c55c36d
commit 3d9622978d
27 changed files with 1790 additions and 78 deletions

View File

@@ -15,7 +15,9 @@ const emit = defineEmits(['mounted', 'callback'])
defineExpose({
pauseResume,
reset
reset,
start,
pause
})
const state = reactive<{

View File

@@ -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>

View File

@@ -0,0 +1,215 @@
<template>
<div ref="editorRef"></div>
</template>
<script setup lang="ts" name="Editor">
import { PropType, watch, computed, onMounted, onBeforeUnmount, ref, unref } from 'vue'
import E from 'wangeditor'
import hljs from 'highlight.js' // 这个蠢货插件9以上的版本都不支持IE。辣鸡
import 'highlight.js/styles/monokai-sublime.css'
import { oneOf } from '@/utils'
import { EditorConfig } from './types'
import { Message } from '_c/Message'
const props = defineProps({
config: {
type: Object as PropType<EditorConfig>,
default: () => {
return {}
}
},
valueType: {
type: String as PropType<'html' | 'text'>,
default: 'html',
validator: (val: string) => {
return oneOf(val, ['html', 'text'])
}
},
value: {
type: String as PropType<string>,
default: ''
}
})
const emit = defineEmits(['change', 'focus', 'blur'])
defineExpose({
getHtml,
getJSON,
getText
})
let editor: Nullable<E> = null
const value = computed(() => props.value)
const editorRef = ref<Nullable<HTMLElement>>(null)
watch(
value,
(val: string) => {
if (editor) {
editor.txt.html(val)
}
},
{
immediate: true
}
)
function createdEditor() {
editor = new E(unref(editorRef.value) as HTMLElement)
initConfig()
editor.create()
editor.txt.html(value.value)
}
function initConfig() {
const config = props.config as EditorConfig
const editorRef = editor as E
// // 设置编辑区域高度为 500px
editorRef.config.height = config.height || 500
// // 设置zIndex
editorRef.config.zIndex = config.zIndex || 0
// // 设置 placeholder 提示文字
editorRef.config.placeholder = config.placeholder || '请输入文本'
// // 设置是否自动聚焦
editorRef.config.focus = config.focus || false
// 配置菜单
editorRef.config.menus = config.menus || [
'head',
'bold',
'fontSize',
'fontName',
'italic',
'underline',
'strikeThrough',
'indent',
'lineHeight',
'foreColor',
'backColor',
'link',
'list',
'justify',
'quote',
'emoticon',
'image',
'video',
'table',
'code',
'splitLine',
'undo',
'redo'
]
// 配置颜色(文字颜色、背景色)
editorRef.config.colors = config.colors || ['#000000', '#eeece0', '#1c487f', '#4d80bf']
// 配置字体
editorRef.config.fontNames = config.fontNames || [
'黑体',
'仿宋',
'楷体',
'标楷体',
'华文仿宋',
'华文楷体',
'宋体',
'微软雅黑',
'Arial',
'Tahoma',
'Verdana',
'Times New Roman',
'Courier New'
]
// 配置行高
editorRef.config.lineHeights = config.lineHeights || ['1', '1.15', '1.6', '2', '2.5', '3']
// // 代码高亮
editorRef.highlight = hljs
// // 配置全屏
editorRef.config.showFullScreen = config.showFullScreen || true
// 编辑器 customAlert 是对全局的alert做了统一处理默认为 window.alert。
// 如觉得浏览器自带的alert体验不佳可自定义 alert以便于达到与自身项目统一的alert效果。
editorRef.config.customAlert =
config.customAlert ||
function (s: string, t: string) {
switch (t) {
case 'success':
Message.success(s)
break
case 'info':
Message.info(s)
break
case 'warning':
Message.warning(s)
break
case 'error':
Message.error(s)
break
default:
Message.info(s)
break
}
}
// 图片上传默认使用base64
editorRef.config.uploadImgShowBase64 = true
// 配置 onchange 回调函数
editorRef.config.onchange = (html: string) => {
const text = editorRef.txt.text()
emitFun(editor, props.valueType === 'html' ? html : text, 'change')
}
// 配置触发 onchange 的时间频率,默认为 200ms
editorRef.config.onchangeTimeout = config.onchangeTimeout || 1000
// 编辑区域 focus聚焦和 blur失焦时触发的回调函数。
editorRef.config.onblur = (html: string) => {
emitFun(editor, html, 'blur')
}
editorRef.config.onfocus = (html: string) => {
emitFun(editor, html, 'focus')
}
}
function emitFun(editor: any, _: string, type: 'change' | 'focus' | 'blur'): void {
if (editor) {
emit(type, props.valueType === 'html' ? (editor as E).txt.html() : (editor as E).txt.text())
}
}
function getHtml() {
if (editor) {
return (editor as E).txt.html()
}
}
function getText() {
if (editor) {
return (editor as E).txt.text()
}
}
function getJSON() {
if (editor) {
return (editor as E).txt.getJSON()
}
}
onMounted(() => {
createdEditor()
})
onBeforeUnmount(() => {
if (editor) {
;(editor as E).destroy()
editor = null
}
})
</script>

View File

@@ -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 // 是否全屏
}

View File

@@ -0,0 +1,267 @@
<template>
<div v-loading="loading" class="qrcode__wrap" :style="wrapStyle">
<component :is="tag" ref="wrapRef" @click="clickCode" />
<div v-if="disabled" class="disabled__wrap" @click="disabledClick">
<div>
<i class="el-icon-refresh-right"></i>
<div>{{ disabledText }}</div>
</div>
</div>
</div>
</template>
<script setup lang="ts" name="Qrcode">
import { PropType, nextTick, ref, watch, computed, unref } from 'vue'
import type { LogoTypes } from './types'
import QRCode from 'qrcode'
import { QRCodeRenderersOptions } from 'qrcode'
import { deepClone } from '@/utils'
import { isString } from '@/utils/validate'
const { toCanvas, toDataURL } = QRCode
const props = defineProps({
// img 或者 canvas,img不支持logo嵌套
tag: {
type: String as PropType<'canvas' | 'img'>,
default: 'canvas',
validator: (v: string) => ['canvas', 'img'].includes(v)
},
// 二维码内容
text: {
type: [String, Array] as PropType<string | any[]>,
default: null
},
// qrcode.js配置项
options: {
type: Object as PropType<QRCodeRenderersOptions>,
default: () => {
return {}
}
},
// 宽度
width: {
type: Number as PropType<number>,
default: 200
},
// logo
logo: {
type: [String, Object] as PropType<Partial<LogoTypes> | string>,
default: ''
},
// 是否过期
disabled: {
type: Boolean as PropType<boolean>,
default: false
},
// 过期提示内容
disabledText: {
type: String as PropType<string>,
default: '二维码已失效'
}
})
const emit = defineEmits(['done', 'click', 'disabled-click'])
const loading = ref<boolean>(true)
const wrapRef = ref<HTMLCanvasElement | HTMLImageElement | null>(null)
const renderText = computed(() => String(props.text))
const wrapStyle = computed(() => {
return {
width: props.width + 'px',
height: props.width + 'px'
}
})
watch(
() => renderText.value,
(val) => {
if (!val) return
initQrcode()
},
{
deep: true,
immediate: true
}
)
// 初始化
function initQrcode() {
nextTick(async () => {
const options = deepClone(props.options || {})
if (props.tag === 'canvas') {
// 容错率,默认对内容少的二维码采用高容错率,内容多的二维码采用低容错率
options.errorCorrectionLevel =
options.errorCorrectionLevel || getErrorCorrectionLevel(renderText.value)
getOriginWidth(renderText.value, options).then(async (_width) => {
options.scale = props.width === 0 ? undefined : (props.width / _width) * 4
const canvasRef: any = await toCanvas(unref(wrapRef as any), renderText.value, options)
if (props.logo) {
const url = await createLogoCode(canvasRef)
emit('done', url)
loading.value = false
} else {
emit('done', canvasRef.toDataURL())
loading.value = false
}
})
} else {
const url = await toDataURL(renderText.value, {
errorCorrectionLevel: 'H',
width: props.width,
...options
})
unref(wrapRef as any).src = url
emit('done', url)
loading.value = false
}
})
}
// 生成logo
function createLogoCode(canvasRef: HTMLCanvasElement) {
const canvasWidth = canvasRef.width
const logoOptions: LogoTypes = Object.assign(
{
logoSize: 0.15,
bgColor: '#ffffff',
borderSize: 0.05,
crossOrigin: 'anonymous',
borderRadius: 8,
logoRadius: 0
},
isString(props.logo) ? {} : props.logo
)
const {
logoSize = 0.15,
bgColor = '#ffffff',
borderSize = 0.05,
crossOrigin = 'anonymous',
borderRadius = 8,
logoRadius = 0
} = logoOptions
const logoSrc = isString(props.logo) ? props.logo : props.logo.src
const logoWidth = canvasWidth * logoSize
const logoXY = (canvasWidth * (1 - logoSize)) / 2
const logoBgWidth = canvasWidth * (logoSize + borderSize)
const logoBgXY = (canvasWidth * (1 - logoSize - borderSize)) / 2
const ctx = canvasRef.getContext('2d')
if (!ctx) return
// logo 底色
canvasRoundRect(ctx)(logoBgXY, logoBgXY, logoBgWidth, logoBgWidth, borderRadius)
ctx.fillStyle = bgColor
ctx.fill()
// logo
const image = new Image()
if (crossOrigin || logoRadius) {
image.setAttribute('crossOrigin', crossOrigin)
}
;(image as any).src = logoSrc
// 使用image绘制可以避免某些跨域情况
const drawLogoWithImage = (image: HTMLImageElement) => {
ctx.drawImage(image, logoXY, logoXY, logoWidth, logoWidth)
}
// 使用canvas绘制以获得更多的功能
const drawLogoWithCanvas = (image: HTMLImageElement) => {
const canvasImage = document.createElement('canvas')
canvasImage.width = logoXY + logoWidth
canvasImage.height = logoXY + logoWidth
const imageCanvas = canvasImage.getContext('2d')
if (!imageCanvas || !ctx) return
imageCanvas.drawImage(image, logoXY, logoXY, logoWidth, logoWidth)
canvasRoundRect(ctx)(logoXY, logoXY, logoWidth, logoWidth, logoRadius)
if (!ctx) return
const fillStyle = ctx.createPattern(canvasImage, 'no-repeat')
if (fillStyle) {
ctx.fillStyle = fillStyle
ctx.fill()
}
}
// 将 logo绘制到 canvas上
return new Promise((resolve: any) => {
image.onload = () => {
logoRadius ? drawLogoWithCanvas(image) : drawLogoWithImage(image)
resolve(canvasRef.toDataURL())
}
})
}
// 得到原QrCode的大小以便缩放得到正确的QrCode大小
function getOriginWidth(content: string, options: QRCodeRenderersOptions) {
const _canvas = document.createElement('canvas')
return toCanvas(_canvas, content, options).then(() => _canvas.width)
}
// 对于内容少的QrCode增大容错率
function getErrorCorrectionLevel(content: string) {
if (content.length > 36) {
return 'M'
} else if (content.length > 16) {
return 'Q'
} else {
return 'H'
}
}
// 点击二维码
function clickCode() {
emit('click')
}
// 失效点击事件
function disabledClick() {
emit('disabled-click')
}
// copy来的方法用于绘制圆角
function canvasRoundRect(ctx: CanvasRenderingContext2D) {
return (x: number, y: number, w: number, h: number, r: number) => {
const minSize = Math.min(w, h)
if (r > minSize / 2) {
r = minSize / 2
}
ctx.beginPath()
ctx.moveTo(x + r, y)
ctx.arcTo(x + w, y, x + w, y + h, r)
ctx.arcTo(x + w, y + h, x, y + h, r)
ctx.arcTo(x, y + h, x, y, r)
ctx.arcTo(x, y, x + w, y, r)
ctx.closePath()
return ctx
}
}
</script>
<style lang="less" scoped>
.qrcode__wrap {
display: inline-block;
position: relative;
.disabled__wrap {
position: absolute;
width: 100%;
height: 100%;
background: rgba(255, 255, 255, 0.95);
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
& > div {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-weight: bold;
i {
font-size: 30px;
margin-bottom: 10px;
}
}
}
}
</style>

View File

@@ -0,0 +1,9 @@
export interface LogoTypes {
src?: string
logoSize?: number
bgColor?: string
borderSize?: number
crossOrigin?: string
borderRadius?: number
logoRadius?: number
}

View File

@@ -0,0 +1,337 @@
<template>
<div :class="{ search__col: layout === 'right' }">
<el-row :gutter="20">
<el-col :span="layout === 'right' ? 22 : 24">
<el-form
ref="ruleForm"
inline
:model="formInline"
:rules="rules"
:label-width="labelWidth"
:label-position="labelPosition"
:hide-required-asterisk="hideRequiredAsterisk"
@submit.prevent
>
<el-form-item
v-for="(item, $index) in data"
:key="$index"
:label="item.label"
:prop="item.field"
:rules="item.rules"
>
<template v-if="item.itemType === 'switch'">
<el-switch
v-model="formInline[item.field]"
v-bind="{ ...getItemBindValue(item) }"
@change="
(val) => {
changeVal(val, item)
}
"
/>
</template>
<template v-if="item.itemType === 'input'">
<el-input
v-model="formInline[item.field]"
v-bind="{ ...getItemBindValue(item) }"
@change="
(val) => {
changeVal(val, item)
}
"
/>
</template>
<template v-if="item.itemType === 'select'">
<el-select
v-model="formInline[item.field]"
v-bind="{ ...getItemBindValue(item) }"
@change="
(val) => {
changeVal(val, item)
}
"
>
<el-option
v-for="v in item.options"
:key="item.optionValue ? v[item.optionValue] : v.value"
:value="item.optionValue ? v[item.optionValue] : v.value"
:label="item.optionLabel ? v[item.optionLabel] : v.title"
/>
</el-select>
</template>
<template v-if="item.itemType === 'radio'">
<el-radio-group
v-model="formInline[item.field]"
@change="
(val) => {
changeVal(val, item)
}
"
>
<template v-if="item.radioType === 'radio'">
<el-radio
v-for="v in item.options"
:key="item.optionValue ? v[item.optionValue] : v.value"
v-bind="{ ...getItemBindValue(item) }"
:label="item.optionValue ? v[item.optionValue] : v.value"
>
{{ item.optionLabel ? v[item.optionLabel] : v.label }}
</el-radio>
</template>
<template v-else-if="item.radioType === 'button'">
<el-radio-button
v-for="v in item.options"
:key="item.optionValue ? v[item.optionValue] : v.value"
v-bind="{ ...getItemBindValue(item) }"
:label="item.optionValue ? v[item.optionValue] : v.value"
>
{{ item.optionLabel ? v[item.optionLabel] : v.label }}
</el-radio-button>
</template>
</el-radio-group>
</template>
<!-- element近期会新增treeSelect组件所以不打算在自己维护一套等待ing -->
<!-- <template v-if="item.itemType === 'treeSelect'">
<el-tree-select
v-model:value="formInline[item.field]"
:size="item.size"
:dropdown-style="item.dropdownStyle"
:tree-data="item.options"
:placeholder="item.placeholder"
:tree-checkable="item.treeCheckable"
:max-tag-count="item.maxTagCount"
:tree-default-expand-all="item.treeDefaultExpandAll"
:allow-clear="item.allowClear"
style="min-width: 201px;"
@change="((val) => {changeVal(val, item)})"
>
<template #title="{ title }">
<span>{{ title }}</span>
</template>
</el-tree-select>
</template> -->
<template v-if="item.itemType === 'timePicker'">
<el-time-picker
v-model="formInline[item.field]"
v-bind="{ ...getItemBindValue(item) }"
@change="
(val) => {
changeVal(val, item)
}
"
/>
</template>
<template v-if="item.itemType === 'timeSelect'">
<el-time-select
v-model="formInline[item.field]"
v-bind="{ ...getItemBindValue(item) }"
@change="
(val) => {
changeVal(val, item)
}
"
/>
</template>
<template v-if="item.itemType === 'datePicker' || item.itemType === 'dateTimePicker'">
<el-date-picker
v-model="formInline[item.field]"
v-bind="{ ...getItemBindValue(item) }"
@change="
(val) => {
changeVal(val, item)
}
"
/>
</template>
</el-form-item>
<el-form-item v-if="data.length > 0 && layout === 'classic'">
<el-button type="primary" icon="el-icon-search" @click="submitForm"> 查询 </el-button>
<el-button v-if="showReset" icon="el-icon-refresh-right" @click="resetForm">
重置
</el-button>
</el-form-item>
</el-form>
</el-col>
<el-col :span="layout === 'right' ? 2 : 24">
<div
v-if="data.length > 0 && (layout === 'bottom' || layout === 'right')"
class="search__bottom"
:class="{ 'search__bottom--col': layout === 'right' }"
>
<div class="search__bottom--button">
<el-button type="primary" icon="el-icon-search" @click="submitForm"> 查询 </el-button>
</div>
<div class="search__bottom--button">
<el-button
v-if="showReset"
:style="{
'margin-left': layout !== 'right' ? '15px' : '0',
'margin-top': layout === 'right' ? '27px' : '0'
}"
icon="el-icon-refresh-right"
@click="resetForm"
>
重置
</el-button>
</div>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup lang="ts" name="Search">
import { PropType, watch, ref, unref } from 'vue'
import { deepClone } from '@/utils'
const props = defineProps({
// 表单域标签的宽度,例如 '50px'。作为 Form 直接子元素的 form-item 会继承该值。支持 auto。
labelWidth: {
type: String as PropType<string>,
default: ''
},
labelPosition: {
type: String as PropType<'right' | 'left' | 'top'>,
default: 'right'
},
// 隐藏所有表单项的必选标记
hideRequiredAsterisk: {
type: Boolean as PropType<boolean>,
default: true
},
// 表单数据对象
data: {
type: Object as PropType<{ [key: string]: any }>,
default: () => {}
},
// 表单验证规则
rules: {
type: Object as PropType<{ [key: number]: any }>,
default: () => {
return {}
}
},
// 是否显示重置按钮
showReset: {
type: Boolean as PropType<boolean>,
default: true
},
// 是否显示导出按钮
showExport: {
type: Boolean as PropType<boolean>,
default: false
},
// 风格
layout: {
type: String as PropType<'classic' | 'bottom' | 'right'>,
default: 'classic'
}
})
const emit = defineEmits(['search-submit', 'reset-submit', 'change'])
const ruleForm = ref<HTMLElement | null>(null)
const formInline = ref<IObj>({})
watch(
() => props.data,
(data) => {
initForm(data)
},
{
deep: true,
immediate: true
}
)
function getItemBindValue(item: any) {
const delArr: string[] = ['label', 'itemType', 'value', 'field']
const obj = deepClone(item)
for (const key in obj) {
if (delArr.indexOf(key) !== -1) {
delete obj[key]
}
}
return obj
}
function initForm(data: any): void {
for (const v of data) {
formInline.value[v.field] = formInline.value[v.field] || v.value
}
}
async function submitForm(): Promise<void> {
const form = unref(ruleForm) as any
if (!form) return
try {
form.validate((valid: boolean) => {
if (valid) {
emit('search-submit', unref(formInline))
} else {
console.log('error submit!!')
return false
}
})
} catch (err) {
console.log(err)
}
}
async function resetForm(): Promise<void> {
const form = unref(ruleForm) as any
if (!form) return
await form.resetFields()
emit('reset-submit', unref(formInline))
}
function changeVal(val: any, item: any): void {
if (item.onChange) {
emit('change', {
field: item.field,
value: val
})
}
}
</script>
<style lang="less" scoped>
.ant-form-inline {
.ant-form-item {
min-height: 60px;
}
.ant-form-item-with-help {
margin-bottom: 0;
}
}
.search__bottom {
text-align: center;
padding-bottom: 20px;
.search__bottom--button {
display: inline-block;
}
}
.search__bottom--col {
padding-bottom: 0;
margin-top: 5px;
position: relative;
.search__bottom--button {
display: inline-block;
}
}
.search__bottom--col::before {
content: '';
width: 1px;
height: 100%;
border-left: 1px solid #d9d9d9;
position: absolute;
top: 0;
left: 0;
}
</style>

View File

@@ -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)
}