初始版本提交
This commit is contained in:
31
src/components/ModalPanel/index.ts
Normal file
31
src/components/ModalPanel/index.ts
Normal file
@ -0,0 +1,31 @@
|
||||
import { withInstall } from '/@/utils';
|
||||
import resizeFixedLayout from './src/ResizeFixedLayout.vue';
|
||||
import ModalPanelVue from './src/ModalPanel.vue';
|
||||
import SearchBoxVue from './src/SearchBox.vue';
|
||||
import EmptyBoxVue from './src/EmptyBox.vue';
|
||||
import NodeHeadVue from './src/NodeHead.vue';
|
||||
import designLogo from './src/DesignLogo.vue';
|
||||
import loadingBox from './src/LoadingBox.vue';
|
||||
|
||||
import fewerLeft from './src/FewerLeft.vue';
|
||||
import fewerRight from './src/FewerRight.vue';
|
||||
import zoomInOrOut from './src/ZoomInOrOut.vue';
|
||||
|
||||
import pageLayout from './src/PageLayout.vue';
|
||||
|
||||
import desktopDesignEmpty from './src/DesktopDesignEmpty.vue';
|
||||
|
||||
export const ResizeFixedLayout = withInstall(resizeFixedLayout);
|
||||
export const DesignLogo = withInstall(designLogo); // 设计页面公司LOGO图标
|
||||
export const DesktopDesignEmpty = withInstall(desktopDesignEmpty); // 桌面设计空数据显示图片
|
||||
export const ModalPanel = withInstall(ModalPanelVue);
|
||||
export const SearchBox = withInstall(SearchBoxVue);
|
||||
export const EmptyBox = withInstall(EmptyBoxVue);
|
||||
export const NodeHead = withInstall(NodeHeadVue); // 标题
|
||||
export const LoadingBox = withInstall(loadingBox); // Loading
|
||||
|
||||
export const FewerLeft = withInstall(fewerLeft); //收取左边图标
|
||||
export const FewerRight = withInstall(fewerRight); //收取右边图标
|
||||
export const ZoomInOrOut = withInstall(zoomInOrOut); //放大缩小
|
||||
|
||||
export const PageLayout = withInstall(pageLayout); // 布局
|
||||
11
src/components/ModalPanel/src/DesignLogo.vue
Normal file
11
src/components/ModalPanel/src/DesignLogo.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<img :src="logoImg" width="208" style="height: 64px" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import logo from '/@/assets/images/design/logo.png';
|
||||
import { useAppStore } from '/@/store/modules/app';
|
||||
const appStore = useAppStore();
|
||||
|
||||
const logoConfig = appStore.getLogoConfig;
|
||||
const logoImg = logoConfig.designerLogoUrl || logo;
|
||||
</script>
|
||||
3
src/components/ModalPanel/src/DesktopDesignEmpty.vue
Normal file
3
src/components/ModalPanel/src/DesktopDesignEmpty.vue
Normal file
@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<img src="./../../../assets/desktop/empty.png" />
|
||||
</template>
|
||||
56
src/components/ModalPanel/src/EmptyBox.vue
Normal file
56
src/components/ModalPanel/src/EmptyBox.vue
Normal file
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div class="wrap">
|
||||
<div class="empty-box">
|
||||
<IconFontSymbol v-if="hasIcon" icon="tishi" class="empty-icon" />
|
||||
<div class="msg">{{ msg }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts">
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const defaultMsg = t('暂无数据');
|
||||
|
||||
export default {};
|
||||
</script>
|
||||
<script setup lang="ts">
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
defineProps({
|
||||
msg: {
|
||||
type: String,
|
||||
default: defaultMsg,
|
||||
},
|
||||
hasIcon: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.wrap {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 80px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.empty-box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.empty-icon {
|
||||
font-size: 36px;
|
||||
}
|
||||
|
||||
.msg {
|
||||
font-size: 14px;
|
||||
color: #9e9d9d;
|
||||
padding: 10px 40px;
|
||||
}
|
||||
</style>
|
||||
9
src/components/ModalPanel/src/FewerLeft.vue
Normal file
9
src/components/ModalPanel/src/FewerLeft.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<img src="./../../../assets/workflow/fewer-left.png" class="img" />
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.img {
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
9
src/components/ModalPanel/src/FewerRight.vue
Normal file
9
src/components/ModalPanel/src/FewerRight.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<img src="./../../../assets/workflow/fewer-right.png" class="img" />
|
||||
</template>
|
||||
<style lang="less" scoped>
|
||||
.img {
|
||||
width: 16px;
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
22
src/components/ModalPanel/src/LoadingBox.vue
Normal file
22
src/components/ModalPanel/src/LoadingBox.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<template>
|
||||
<div class="fix">
|
||||
<a-spin size="large" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.fix {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
103
src/components/ModalPanel/src/ModalPanel.vue
Normal file
103
src/components/ModalPanel/src/ModalPanel.vue
Normal file
@ -0,0 +1,103 @@
|
||||
<template>
|
||||
<a-modal
|
||||
:visible="visible"
|
||||
:title="title"
|
||||
:maskClosable="false"
|
||||
:width="hasLeftSlot ? 1200 : width || 600"
|
||||
:okText="t('确定')"
|
||||
:cancelText="t('取消')"
|
||||
@ok="$emit('submit')"
|
||||
@cancel="$emit('close')"
|
||||
>
|
||||
<slot name="header"></slot>
|
||||
<div class="content">
|
||||
<div :class="['left', isDeptSelect ? 'left-box' : '']" v-if="hasLeftSlot">
|
||||
<slot name="left"></slot>
|
||||
</div>
|
||||
<div class="right">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</a-modal>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
defineEmits(['submit', 'close']);
|
||||
defineProps({
|
||||
title: String,
|
||||
width: Number,
|
||||
visible: { type: Boolean, default: false },
|
||||
isDeptSelect: { type: Boolean, default: false },
|
||||
});
|
||||
const hasLeftSlot = computed(() => {
|
||||
return !!useSlots().left;
|
||||
});
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.content {
|
||||
height: 520px;
|
||||
display: flex;
|
||||
margin: 10px;
|
||||
overflow: auto;
|
||||
|
||||
.left {
|
||||
flex-basis: 310px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.search-box) {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
:deep(.list-box) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow-y: auto;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
:deep(.list-page-box) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
overflow-y: auto;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
:deep(.page-box) {
|
||||
position: absolute;
|
||||
bottom: 80px;
|
||||
right: 20px;
|
||||
}
|
||||
|
||||
:deep(.ant-spin-nested-loading) {
|
||||
height: 480px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
:deep(.title) {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
padding: 0 0 10px 10px;
|
||||
margin: 0 20px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-inner) {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
:deep(.ant-checkbox-checked .ant-checkbox-inner) {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.left-box {
|
||||
margin-left: calc(50% - 155px);
|
||||
}
|
||||
</style>
|
||||
21
src/components/ModalPanel/src/NodeHead.vue
Normal file
21
src/components/ModalPanel/src/NodeHead.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<template>
|
||||
<div class="node-head">
|
||||
<div class="node-title">{{ nodeName }}</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps(['nodeName']);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.node-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.node-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
</style>
|
||||
182
src/components/ModalPanel/src/PageLayout.vue
Normal file
182
src/components/ModalPanel/src/PageLayout.vue
Normal file
@ -0,0 +1,182 @@
|
||||
<template>
|
||||
<div class="layout" :class="layoutClass || ''">
|
||||
<div class="left-width overflow-hidden" v-if="hasLeftSlot">
|
||||
<slot name="left"></slot>
|
||||
<!-- 左侧树 -->
|
||||
</div>
|
||||
<div
|
||||
class="p-3 bg-color"
|
||||
:class="[hasLeftSlot ? 'right-width' : 'full-width']"
|
||||
v-if="hasRightSlot"
|
||||
>
|
||||
<slot name="search"></slot>
|
||||
<!-- 搜索栏 -->
|
||||
<div v-if="layoutOne">
|
||||
<div v-if="props.title" class=" ">
|
||||
<div v-if="props.title" class="l-panel--title">{{ props.title }}</div>
|
||||
<div class="flex justify-between">
|
||||
<SearchBox
|
||||
class="w-full"
|
||||
v-if="hasSearchSlot"
|
||||
:searchConfig="props.searchConfig"
|
||||
@search="search"
|
||||
@scroll-height="$emit('scrollHeight')"
|
||||
/>
|
||||
<slot name="operation"></slot>
|
||||
</div>
|
||||
<!-- 搜索下操作按钮 -->
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div v-if="hasOperationSlot || props.title || hasSearchSlot" class="node-box">
|
||||
<NodeHead v-if="props.title" class="mb-3 mt-3" :nodeName="props.title" />
|
||||
<div class="flex">
|
||||
<SearchBox
|
||||
v-if="hasSearchSlot"
|
||||
:searchConfig="props.searchConfig"
|
||||
@search="search"
|
||||
@scroll-height="$emit('scrollHeight')"
|
||||
/>
|
||||
<slot name="operation"></slot>
|
||||
</div>
|
||||
<!-- 搜索下操作按钮 -->
|
||||
</div>
|
||||
</div>
|
||||
<slot name="right"></slot>
|
||||
<!-- 右边 table列表 -->
|
||||
</div>
|
||||
<div v-if="hasFullSlot" :class="hasLeftSlot ? 'right-width' : 'full-width'">
|
||||
<slot name="full"></slot>
|
||||
<!-- 列表<无搜索、按钮> -->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, useSlots } from 'vue';
|
||||
import { NodeHead } from '/@/components/ModalPanel/index';
|
||||
import SearchBox from './WorkflowSearchBox.vue';
|
||||
const props = defineProps(['title', 'searchConfig', 'layoutClass', 'layoutOne']);
|
||||
const emits = defineEmits(['search', 'scrollHeight']);
|
||||
const hasLeftSlot = computed(() => {
|
||||
return !!useSlots().left;
|
||||
});
|
||||
const hasSearchSlot = computed(() => {
|
||||
return !!useSlots().search;
|
||||
});
|
||||
const hasOperationSlot = computed(() => {
|
||||
return !!useSlots().operation;
|
||||
});
|
||||
const hasRightSlot = computed(() => {
|
||||
return !!useSlots().right;
|
||||
});
|
||||
const hasFullSlot = computed(() => {
|
||||
return !!useSlots().full;
|
||||
});
|
||||
function search(params: any) {
|
||||
emits('search', params);
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.layout {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.left-width {
|
||||
flex-basis: 20%;
|
||||
border-right: 1px solid #e5e7eb;
|
||||
}
|
||||
|
||||
.right-width {
|
||||
flex-basis: 80%;
|
||||
}
|
||||
|
||||
.full-width {
|
||||
flex-basis: 100%;
|
||||
}
|
||||
|
||||
.bg-color {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.node-box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.button-box) {
|
||||
button {
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
// 操作列
|
||||
|
||||
:deep(.delete-icon) {
|
||||
cursor: pointer;
|
||||
color: @clear-color;
|
||||
}
|
||||
|
||||
:deep(.edit-icon) {
|
||||
cursor: pointer;
|
||||
color: @primary-color;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
// 左侧树结构样式
|
||||
:deep(.vben-tree-header) {
|
||||
height: 60px;
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
}
|
||||
|
||||
:deep(.ant-tree) {
|
||||
padding: 0;
|
||||
margin-top: 10px;
|
||||
border-top: 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-switcher) {
|
||||
width: 0;
|
||||
}
|
||||
|
||||
:deep(.ant-tree-title .anticon) {
|
||||
padding-left: 10px;
|
||||
}
|
||||
|
||||
:deep(.ant-tree .ant-tree-treenode) {
|
||||
height: 40px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
// 表
|
||||
:deep(.ant-table-pagination.ant-pagination) {
|
||||
position: fixed;
|
||||
bottom: 10px;
|
||||
right: 30px;
|
||||
}
|
||||
|
||||
.l-panel--title {
|
||||
align-items: center;
|
||||
color: #262626;
|
||||
font-size: 16px;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
border-bottom: 1px solid #d9d9d9;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
padding-bottom: 32px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
</style>
|
||||
<style>
|
||||
html[data-theme='dark'] {
|
||||
.bg-color {
|
||||
background-color: #151515 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
160
src/components/ModalPanel/src/ResizeFixedLayout.vue
Normal file
160
src/components/ModalPanel/src/ResizeFixedLayout.vue
Normal file
@ -0,0 +1,160 @@
|
||||
<template>
|
||||
<div class="flow-containers">
|
||||
<div class="resize-head"><slot name="head"></slot></div>
|
||||
<div class="resize-layout">
|
||||
<div class="resize-left" ref="left"><slot name="left"></slot></div>
|
||||
<div class="resize-shrink-sidebar" title="收缩侧边栏">
|
||||
<span class="shrink-sidebar-text" v-if="showPanel">⋮</span>
|
||||
</div>
|
||||
<div class="resize-right" ref="right">
|
||||
<div class="right-box">
|
||||
<slot name="right"></slot>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fewer-panel-box" @click="changeShowPanel">
|
||||
<component :is="fewerPanelComponent" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import FewerLeft from './FewerLeft.vue';
|
||||
import FewerRight from './FewerRight.vue';
|
||||
let left = ref();
|
||||
let right = ref();
|
||||
let showPanel = ref(true);
|
||||
let fewerPanelComponent = computed(() => {
|
||||
return showPanel.value ? FewerRight : FewerLeft;
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
dragControllerDiv();
|
||||
});
|
||||
function changeShowPanel() {
|
||||
showPanel.value = !showPanel.value;
|
||||
if (showPanel.value) {
|
||||
showRightBox();
|
||||
} else {
|
||||
hideRightBox();
|
||||
}
|
||||
}
|
||||
function showRightBox() {
|
||||
left.value.style.width = '65%';
|
||||
right.value.style.width = '35%';
|
||||
}
|
||||
function hideRightBox() {
|
||||
left.value.style.width = '100%';
|
||||
right.value.style.width = '0';
|
||||
}
|
||||
function changeWidth(moveLen: number, boxClientWidth: number) {
|
||||
left.value.style.width = moveLen + 'px';
|
||||
right.value.style.width = boxClientWidth - moveLen - 10 + 'px';
|
||||
}
|
||||
function dragControllerDiv() {
|
||||
let resize = document.getElementsByClassName('resize-shrink-sidebar') as unknown as Array<any>;
|
||||
let box = document.getElementsByClassName('resize-layout');
|
||||
for (let i = 0; i < resize.length; i++) {
|
||||
resize[i].onmousedown = function (e: { clientX: any }) {
|
||||
let startX = e.clientX;
|
||||
resize[i].left = resize[i].offsetLeft;
|
||||
document.onmousemove = function (e) {
|
||||
let endX = e.clientX;
|
||||
let moveLen = resize[i].left + (endX - startX);
|
||||
let maxT = box[i].clientWidth - resize[i].offsetWidth;
|
||||
|
||||
if (moveLen < 32) moveLen = 32;
|
||||
if (moveLen > maxT - 400) moveLen = maxT - 400;
|
||||
|
||||
resize[i].style.left = moveLen;
|
||||
changeWidth(moveLen, box[i].clientWidth);
|
||||
};
|
||||
document.onmouseup = function () {
|
||||
document.onmousemove = null;
|
||||
document.onmouseup = null;
|
||||
resize[i].releaseCapture && resize[i].releaseCapture();
|
||||
};
|
||||
resize[i].setCapture && resize[i].setCapture();
|
||||
return false;
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.flow-containers {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .flow-containers {
|
||||
background-color: #151515;
|
||||
}
|
||||
|
||||
.head {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.resize-layout {
|
||||
display: flex;
|
||||
height: calc(100% - 50px);
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.resize-left {
|
||||
width: 65%;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.resize-shrink-sidebar {
|
||||
cursor: col-resize;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.shrink-sidebar-text {
|
||||
border-radius: 4px;
|
||||
/* color: #fff; */
|
||||
}
|
||||
|
||||
.shrink-sidebar:hover {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.resize-right {
|
||||
width: 35%; /*右侧初始化宽度*/
|
||||
}
|
||||
|
||||
.right-box {
|
||||
height: 100%;
|
||||
box-shadow: -8px 2px 4px rgb(0 0 0 / 10%);
|
||||
}
|
||||
|
||||
.right-box:hover {
|
||||
box-shadow: -4px 2px 4px rgb(0 0 0 / 10%);
|
||||
border-left: 8px solid #fff;
|
||||
margin-top: -2px;
|
||||
padding-top: 2px;
|
||||
margin-left: -8px;
|
||||
}
|
||||
|
||||
[data-theme='dark'] .right-box:hover {
|
||||
border-left: 8px solid #151515;
|
||||
}
|
||||
|
||||
.fewer-panel-box {
|
||||
width: 20px;
|
||||
position: fixed;
|
||||
top: 80px;
|
||||
right: 10px;
|
||||
z-index: 3;
|
||||
}
|
||||
</style>
|
||||
32
src/components/ModalPanel/src/SearchBox.vue
Normal file
32
src/components/ModalPanel/src/SearchBox.vue
Normal file
@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<div class="search-box">
|
||||
<a-input v-model:value="keyword" :placeholder="t('请填写搜索内容')" style="width: 300px" />
|
||||
<a-button type="primary" @click="search" class="serach-btn">{{ t('搜索') }}</a-button>
|
||||
<a-button @click="cancel" class="serach-btn">{{ t('重置') }}</a-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const emits = defineEmits(['search']);
|
||||
let keyword = ref('');
|
||||
function search() {
|
||||
emits('search', keyword.value);
|
||||
}
|
||||
function cancel() {
|
||||
keyword.value = '';
|
||||
emits('search', keyword.value);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.search-box {
|
||||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
.serach-btn {
|
||||
margin-left: 10px;
|
||||
}
|
||||
</style>
|
||||
217
src/components/ModalPanel/src/WorkflowSearchBox.vue
Normal file
217
src/components/ModalPanel/src/WorkflowSearchBox.vue
Normal file
@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div class="search">
|
||||
<div
|
||||
v-for="(item, index) in searchData.list"
|
||||
:key="index"
|
||||
class="search-item"
|
||||
:style="item.type == 'date' ? 'flex-basis: 48%' : ''"
|
||||
>
|
||||
<!-- 搜索按钮 -->
|
||||
<div class="item" v-if="item.type == 'search'">
|
||||
<a-button type="primary" @click="search" class="search-btn">{{ t('搜索') }}</a-button>
|
||||
<a-button @click="reset" class="search-btn">{{ t('重置') }}</a-button>
|
||||
<span @click="expansionOrContraction" class="show-icon" v-if="searchData.showSearchIcon"
|
||||
>{{ searchData.showAll ? t('收缩') : t('展开') }}
|
||||
<Icon icon="ion:chevron-forward" :class="searchData.showAll ? 'show' : 'hide'"
|
||||
/></span>
|
||||
</div>
|
||||
<!-- 时间范围 -->
|
||||
<div class="item" v-else-if="item.type == 'date'">
|
||||
<div class="label" v-if="item.label">{{ item.label }}:</div>
|
||||
<a-range-picker
|
||||
v-model:value="searchData.searchForm[item.field]"
|
||||
:format="dateFormat"
|
||||
:valueFormat="dateFormat"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
<!-- 用户 -->
|
||||
<div class="item" v-else-if="item.type == 'user'">
|
||||
<div class="label" v-if="item.label">{{ item.label }}:</div>
|
||||
<SelectUser
|
||||
:selectedIds="searchData.searchForm[item.field]"
|
||||
:multiple="true"
|
||||
@change="
|
||||
(ids) => {
|
||||
searchData.searchForm[item.field] = ids;
|
||||
}
|
||||
"
|
||||
@change-names="
|
||||
(names) => {
|
||||
searchData.showData[item.field] = names;
|
||||
}
|
||||
"
|
||||
>
|
||||
<a-input
|
||||
:value="searchData.showData[item.field]"
|
||||
:placeholder="item.label"
|
||||
style="width: 100%"
|
||||
>
|
||||
<template #addonAfter>
|
||||
<Icon icon="ant-design:user-outlined" />
|
||||
</template>
|
||||
</a-input>
|
||||
</SelectUser>
|
||||
</div>
|
||||
|
||||
<div class="item" v-else>
|
||||
<div class="label" v-if="item.label">{{ item.label }}:</div>
|
||||
<a-input
|
||||
v-model:value="searchData.searchForm[item.field]"
|
||||
:placeholder="t('请输入') + item.label"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted, reactive } from 'vue';
|
||||
import { Icon } from '/@/components/Icon/index';
|
||||
import { SelectUser } from '/@/components/SelectOrganizational/index';
|
||||
import { cloneDeep } from 'lodash-es';
|
||||
import { useI18n } from '/@/hooks/web/useI18n';
|
||||
const { t } = useI18n();
|
||||
const dateFormat = 'YYYY-MM-DD HH:mm:ss';
|
||||
const props = defineProps(['searchConfig']);
|
||||
const emits = defineEmits(['search', 'scrollHeight']);
|
||||
// 搜索
|
||||
onMounted(() => {
|
||||
initSearch();
|
||||
});
|
||||
let searchData: {
|
||||
showAll: boolean;
|
||||
showSearchIcon: boolean;
|
||||
searchForm: any;
|
||||
showData: any;
|
||||
originalData: Array<{ field: string; label: string; value: string; type: string }>;
|
||||
list: Array<{ field: string; label: string; value: string; type: string }>;
|
||||
} = reactive({
|
||||
showAll: false,
|
||||
showSearchIcon: true,
|
||||
originalData: [],
|
||||
list: [],
|
||||
searchForm: {},
|
||||
showData: {},
|
||||
});
|
||||
|
||||
function initSearch() {
|
||||
if (props.searchConfig) {
|
||||
searchData.showAll = true;
|
||||
searchData.originalData = cloneDeep(props.searchConfig);
|
||||
searchData.originalData.forEach((element) => {
|
||||
if (element.field === 'originator' || element.field === 'userID') {
|
||||
searchData.searchForm[element.field] = [];
|
||||
searchData.showData[element.field] = '';
|
||||
} else if (element.field === 'searchDate') {
|
||||
searchData.searchForm[element.field] = [null, null];
|
||||
} else {
|
||||
searchData.searchForm[element.field] = element.value ? element.value : '';
|
||||
}
|
||||
});
|
||||
expansionOrContraction();
|
||||
}
|
||||
}
|
||||
function search() {
|
||||
let params = {};
|
||||
for (const key in searchData.searchForm) {
|
||||
if (Object.prototype.hasOwnProperty.call(searchData.searchForm, key)) {
|
||||
const value = searchData.searchForm[key];
|
||||
if (key === 'originator') {
|
||||
//发起人
|
||||
if (Array.isArray(value)) {
|
||||
params['originator'] = value.join(',');
|
||||
}
|
||||
} else if (key === 'searchDate') {
|
||||
if (Array.isArray(value)) {
|
||||
params['startTime'] = value[0];
|
||||
params['endTime'] = value[1];
|
||||
}
|
||||
} else {
|
||||
params[key] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
emits('search', params);
|
||||
}
|
||||
function reset() {
|
||||
initSearch();
|
||||
search();
|
||||
}
|
||||
// 展开或者收缩
|
||||
function expansionOrContraction() {
|
||||
searchData.showAll = !searchData.showAll;
|
||||
if (searchData.originalData.length > 3) {
|
||||
searchData.showSearchIcon = true;
|
||||
searchData.list = cloneDeep(searchData.originalData).splice(0, 3);
|
||||
searchData.list.push({
|
||||
field: '',
|
||||
label: '',
|
||||
type: 'search',
|
||||
value: '',
|
||||
});
|
||||
if (searchData.showAll) {
|
||||
searchData.list.push(
|
||||
...cloneDeep(searchData.originalData).splice(3, searchData.originalData.length),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
searchData.showSearchIcon = false;
|
||||
searchData.list = cloneDeep(searchData.originalData);
|
||||
searchData.list.push({
|
||||
field: '',
|
||||
label: '',
|
||||
type: 'search',
|
||||
value: '',
|
||||
});
|
||||
}
|
||||
emits('scrollHeight');
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
// 搜索
|
||||
.search {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
|
||||
.search-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.show-icon {
|
||||
color: @primary-color;
|
||||
margin: 0 10px;
|
||||
align-self: center;
|
||||
}
|
||||
|
||||
.show {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.hide {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.label {
|
||||
min-width: 70px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
33
src/components/ModalPanel/src/ZoomInOrOut.vue
Normal file
33
src/components/ModalPanel/src/ZoomInOrOut.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div class="fixed-bottom-item">
|
||||
<div class="fixed-common-btn" @click="$emit('out')">
|
||||
<IconFontSymbol icon="fangda" />
|
||||
</div>
|
||||
<div class="fixed-common-btn" @click="$emit('in')">
|
||||
<IconFontSymbol icon="suoxiao" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
|
||||
defineEmits(['in', 'out']);
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.fixed-bottom-item {
|
||||
display: flex;
|
||||
box-shadow: 0px 2px 2px 2px rgb(0 0 0 / 4%);
|
||||
border-radius: 4px;
|
||||
padding: 4px;
|
||||
margin-right: 10px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.fixed-common-btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user