joy-admin-components 0.1.25 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/joy-admin-components.es.js +38 -0
- package/dist/joy-admin-components.umd.js +1 -0
- package/package.json +7 -5
- package/src/index.d.ts +15 -0
- package/src/components/CmpDictionary/index.d.ts +0 -43
- package/src/components/CmpDictionary/index.vue +0 -162
- package/src/components/ConfrimButton/index.d.ts +0 -19
- package/src/components/ConfrimButton/index.vue +0 -30
- package/src/components/ImportButton/index.d.ts +0 -20
- package/src/components/ImportButton/index.vue +0 -50
- package/src/components/LayOutForm/index.d.ts +0 -20
- package/src/components/LayOutForm/index.vue +0 -41
- package/src/components/ListPage/index.d.ts +0 -92
- package/src/components/ListPage/index.vue +0 -258
- package/src/components/SearchBar/index.d.ts +0 -51
- package/src/components/SearchBar/index.vue +0 -112
- package/src/components/SearchBar/tools.js +0 -39
- package/src/components/VxeTable/index.jsx +0 -54
- package/src/components/VxeTable/render/EnumRender.vue +0 -33
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { ElForm as d, ElRow as i, ElCol as s } from "element-plus";
|
|
2
|
+
import { defineComponent as c, ref as y, onMounted as g, h as r } from "vue";
|
|
3
|
+
const u = c({
|
|
4
|
+
name: "JoyForm",
|
|
5
|
+
setup(o, { slots: t, attrs: n, emit: m }) {
|
|
6
|
+
const f = y(null), a = () => t.default ? t.default().filter((e) => e.type !== Symbol.for("v-cmt")).map((e) => e.type === Symbol.for("v-fgt") ? e.children : e).flat().map(
|
|
7
|
+
(e) => {
|
|
8
|
+
var l;
|
|
9
|
+
return r(
|
|
10
|
+
s,
|
|
11
|
+
{ span: ((l = e.props) == null ? void 0 : l.span) || 24 },
|
|
12
|
+
{
|
|
13
|
+
default: () => e
|
|
14
|
+
}
|
|
15
|
+
);
|
|
16
|
+
}
|
|
17
|
+
) : null;
|
|
18
|
+
g(() => {
|
|
19
|
+
m("ref", f.value);
|
|
20
|
+
});
|
|
21
|
+
function p() {
|
|
22
|
+
return r(
|
|
23
|
+
d,
|
|
24
|
+
{ ...n, ref: f },
|
|
25
|
+
() => r(i, { gutter: n.gutter ?? 20 }, { default: () => a() })
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
return p;
|
|
29
|
+
}
|
|
30
|
+
}), S = {
|
|
31
|
+
install: (o) => {
|
|
32
|
+
o.component(u.name, u);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
export {
|
|
36
|
+
u as JoyForm,
|
|
37
|
+
S as default
|
|
38
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(function(e,t){typeof exports=="object"&&typeof module<"u"?t(exports,require("element-plus"),require("vue")):typeof define=="function"&&define.amd?define(["exports","element-plus","vue"],t):(e=typeof globalThis<"u"?globalThis:e||self,t(e.JoyAdminComponents={},e.ElementPlus,e.Vue))})(this,function(e,t,o){"use strict";const r=o.defineComponent({name:"JoyForm",setup(f,{slots:u,attrs:i,emit:m}){const l=o.ref(null),s=()=>u.default?u.default().filter(n=>n.type!==Symbol.for("v-cmt")).map(n=>n.type===Symbol.for("v-fgt")?n.children:n).flat().map(n=>{var d;return o.h(t.ElCol,{span:((d=n.props)==null?void 0:d.span)||24},{default:()=>n})}):null;o.onMounted(()=>{m("ref",l.value)});function a(){return o.h(t.ElForm,{...i,ref:l},()=>o.h(t.ElRow,{gutter:i.gutter??20},{default:()=>s()}))}return a}}),p={install:f=>{f.component(r.name,r)}};e.JoyForm=r,e.default=p,Object.defineProperties(e,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})});
|
package/package.json
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "joy-admin-components",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"main": "./dist/joy-admin-components.umd.js",
|
|
5
5
|
"module": "./dist/joy-admin-components.es.js",
|
|
6
6
|
"types": "./src/index.d.ts",
|
|
7
7
|
"exports": {
|
|
8
|
-
"
|
|
9
|
-
"types": "./src/
|
|
10
|
-
"import": "./
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./src/index.d.ts",
|
|
10
|
+
"import": "./dist/joy-admin-components.es.js",
|
|
11
|
+
"require": "./dist/joy-admin-components.umd.js"
|
|
11
12
|
}
|
|
12
13
|
},
|
|
13
14
|
"files": [
|
|
14
|
-
"
|
|
15
|
+
"dist",
|
|
16
|
+
"src/index.d.ts"
|
|
15
17
|
],
|
|
16
18
|
"scripts": {
|
|
17
19
|
"dev": "vite",
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// 导出各组件的类型
|
|
2
|
+
export * from './components/LayOutForm/index';
|
|
3
|
+
export * from './components/CmpDictionary/index';
|
|
4
|
+
export * from './components/ConfrimButton/index';
|
|
5
|
+
export * from './components/ImportButton/index';
|
|
6
|
+
export * from './components/ListPage/index';
|
|
7
|
+
export * from './components/SearchBar/index';
|
|
8
|
+
|
|
9
|
+
// 导出组件
|
|
10
|
+
export { default as LayOutForm } from './components/LayOutForm/index.vue';
|
|
11
|
+
export { default as CmpDictionary } from './components/CmpDictionary/index.vue';
|
|
12
|
+
export { default as ConfrimButton } from './components/ConfrimButton/index.vue';
|
|
13
|
+
export { default as ImportButton } from './components/ImportButton/index.vue';
|
|
14
|
+
export { default as ListPage } from './components/ListPage/index.vue';
|
|
15
|
+
export { default as SearchBar } from './components/SearchBar/index.vue';
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import { DefineComponent } from 'vue';
|
|
2
|
-
import { SelectProps } from 'element-plus';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 字典选择器组件 - 基于 Element Plus Select 封装
|
|
6
|
-
* 支持 API 请求数据、全选、多语言等功能
|
|
7
|
-
*/
|
|
8
|
-
export interface CmpDictionaryProps extends /* @vue-ignore */ Partial<SelectProps> {
|
|
9
|
-
/** 获取下拉数据的 API 函数 */
|
|
10
|
-
api?: () => Promise<{ data: any[] }>;
|
|
11
|
-
/** 是否显示全选复选框(仅多选模式),默认 true */
|
|
12
|
-
showCheckAll?: boolean;
|
|
13
|
-
/** 选项点击回调函数 */
|
|
14
|
-
optionClick?: (item: any) => void;
|
|
15
|
-
/** 多选模式下的最大选择数量 */
|
|
16
|
-
maxLimit?: number;
|
|
17
|
-
/** 多选模式下的最小选择数量 */
|
|
18
|
-
minLimit?: number;
|
|
19
|
-
/** 静态数据源(如果提供则不使用 api) */
|
|
20
|
-
data?: any[];
|
|
21
|
-
/** 字段映射配置 */
|
|
22
|
-
labelValue?: {
|
|
23
|
-
label?: string;
|
|
24
|
-
labelEn?: string;
|
|
25
|
-
value?: string;
|
|
26
|
-
};
|
|
27
|
-
/** 是否使用本地 i18n 翻译 label,默认 false */
|
|
28
|
-
changeLocal?: boolean;
|
|
29
|
-
/** v-model 绑定值 */
|
|
30
|
-
modelValue?: any;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface CmpDictionaryEmits {
|
|
34
|
-
/** API 请求成功后触发 */
|
|
35
|
-
(e: 'success', data: any[]): void;
|
|
36
|
-
/** 选择值变化时触发 */
|
|
37
|
-
(e: 'change', value: any): void;
|
|
38
|
-
/** v-model 更新事件 */
|
|
39
|
-
(e: 'update:modelValue', value: any): void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
declare const CmpDictionary: DefineComponent<CmpDictionaryProps, {}, any, {}, {}, {}, {}, CmpDictionaryEmits>;
|
|
43
|
-
export default CmpDictionary;
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<el-select
|
|
3
|
-
v-model="model"
|
|
4
|
-
v-bind="$attrs"
|
|
5
|
-
filterable
|
|
6
|
-
collapse-tags
|
|
7
|
-
collapse-tags-tooltip
|
|
8
|
-
clearable
|
|
9
|
-
@clear="onChange"
|
|
10
|
-
@change="onChange"
|
|
11
|
-
>
|
|
12
|
-
<slot
|
|
13
|
-
name="header"
|
|
14
|
-
v-if="$attrs.multiple != undefined && $attrs.multiple != false && showCheckAll"
|
|
15
|
-
>
|
|
16
|
-
<el-checkbox class="mgl20" v-model="checkAll" @change="checkAllHandle">
|
|
17
|
-
{{ $t('quan-xuan') }}
|
|
18
|
-
</el-checkbox>
|
|
19
|
-
</slot>
|
|
20
|
-
<el-option
|
|
21
|
-
v-for="(item, index) in data ? data : selectionData"
|
|
22
|
-
:key="index"
|
|
23
|
-
:disabled="getDisabled(item)"
|
|
24
|
-
:label="cmpLabel(item)"
|
|
25
|
-
@click.stop="optionClickHanlde(item)"
|
|
26
|
-
:value="$attrs['value-key'] ? item : item[labelValue.value]"
|
|
27
|
-
></el-option>
|
|
28
|
-
</el-select>
|
|
29
|
-
</template>
|
|
30
|
-
|
|
31
|
-
<script setup>
|
|
32
|
-
import { difference } from 'lodash';
|
|
33
|
-
import { computed, nextTick, ref, useAttrs, watch } from 'vue';
|
|
34
|
-
import { useI18n } from 'vue-i18n';
|
|
35
|
-
const props = defineProps({
|
|
36
|
-
api: {
|
|
37
|
-
type: Function,
|
|
38
|
-
},
|
|
39
|
-
showCheckAll: {
|
|
40
|
-
type: Boolean,
|
|
41
|
-
default: true,
|
|
42
|
-
},
|
|
43
|
-
optionClick: {
|
|
44
|
-
type: Function,
|
|
45
|
-
},
|
|
46
|
-
maxLimit: {
|
|
47
|
-
type: Number,
|
|
48
|
-
default: undefined,
|
|
49
|
-
},
|
|
50
|
-
minLimit: {
|
|
51
|
-
type: Number,
|
|
52
|
-
default: undefined,
|
|
53
|
-
},
|
|
54
|
-
data: {
|
|
55
|
-
type: Array,
|
|
56
|
-
},
|
|
57
|
-
labelValue: {
|
|
58
|
-
type: Object,
|
|
59
|
-
default: () => ({
|
|
60
|
-
label: 'label',
|
|
61
|
-
labelEn: 'labelEn',
|
|
62
|
-
value: 'value',
|
|
63
|
-
}),
|
|
64
|
-
},
|
|
65
|
-
changeLocal: {
|
|
66
|
-
type: Boolean,
|
|
67
|
-
default: false,
|
|
68
|
-
},
|
|
69
|
-
});
|
|
70
|
-
const $attrs = useAttrs();
|
|
71
|
-
const { t, locale } = useI18n();
|
|
72
|
-
const model = defineModel();
|
|
73
|
-
const checkAll = computed({
|
|
74
|
-
get() {
|
|
75
|
-
if ($attrs.multiple == undefined) return false;
|
|
76
|
-
if (model.value == undefined) return false;
|
|
77
|
-
let data = props.data ? props.data : selectionData.value;
|
|
78
|
-
let dif =
|
|
79
|
-
difference(
|
|
80
|
-
data.map((item) => ($attrs['value-key'] ? item : item[props.labelValue.value])),
|
|
81
|
-
model.value,
|
|
82
|
-
).length == 0;
|
|
83
|
-
return model.value.length == data.length && dif;
|
|
84
|
-
},
|
|
85
|
-
set(val) {
|
|
86
|
-
return val;
|
|
87
|
-
},
|
|
88
|
-
});
|
|
89
|
-
const emits = defineEmits(['success', 'change']);
|
|
90
|
-
const selectionData = ref([]);
|
|
91
|
-
const apiGetDaata = async () => {
|
|
92
|
-
const { data } = await props.api();
|
|
93
|
-
selectionData.value = [...data];
|
|
94
|
-
emits('success', selectionData);
|
|
95
|
-
};
|
|
96
|
-
if (props.api) {
|
|
97
|
-
apiGetDaata();
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const labels = props.labelValue.label.split('-');
|
|
101
|
-
// label 可以用-拼接多个属性
|
|
102
|
-
const cmpLabel = (row) => {
|
|
103
|
-
if (labels.length == 1) {
|
|
104
|
-
// props.changeLocal 使用本地翻译
|
|
105
|
-
if (props.changeLocal) {
|
|
106
|
-
return t(row[props.labelValue.label]);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// 取设置的labelEn
|
|
110
|
-
let label =
|
|
111
|
-
locale.value == 'en_us'
|
|
112
|
-
? row[props.labelValue.labelEn || 'i18nName']
|
|
113
|
-
: row[props.labelValue.label];
|
|
114
|
-
// 中文兜底
|
|
115
|
-
label = label || row[props.labelValue.label];
|
|
116
|
-
return label;
|
|
117
|
-
} else {
|
|
118
|
-
let res = [];
|
|
119
|
-
labels.forEach((label) => {
|
|
120
|
-
res.push(row[label]);
|
|
121
|
-
});
|
|
122
|
-
return res.join('-');
|
|
123
|
-
}
|
|
124
|
-
};
|
|
125
|
-
function checkAllHandle(val) {
|
|
126
|
-
let data = props.data ? props.data : selectionData.value;
|
|
127
|
-
model.value = val
|
|
128
|
-
? data.map((item) => ($attrs['value-key'] ? item : item[props.labelValue.value]))
|
|
129
|
-
: [];
|
|
130
|
-
nextTick(() => {
|
|
131
|
-
emits('change', model.value);
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
function optionClickHanlde(item) {
|
|
135
|
-
props.optionClick && props.optionClick(item);
|
|
136
|
-
props.optionClick && emits('change', $attrs['value-key'] ? item : item[props.labelValue.value]);
|
|
137
|
-
}
|
|
138
|
-
function onChange(val) {
|
|
139
|
-
emits('change', val);
|
|
140
|
-
}
|
|
141
|
-
function getDisabled(item) {
|
|
142
|
-
// 多选 最大选择数
|
|
143
|
-
if ($attrs.multiple != undefined && $attrs.multiple != false && props.maxLimit > 0) {
|
|
144
|
-
return (
|
|
145
|
-
model.value.length >= props.maxLimit && !model.value.includes(item[props.labelValue.value])
|
|
146
|
-
);
|
|
147
|
-
}
|
|
148
|
-
// 多选 最小选择数
|
|
149
|
-
if ($attrs.multiple != undefined && $attrs.multiple != false && props.minLimit > 0) {
|
|
150
|
-
return (
|
|
151
|
-
model.value.length <= props.minLimit && model.value.includes(item[props.labelValue.value])
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
</script>
|
|
157
|
-
|
|
158
|
-
<style lang="scss" scoped>
|
|
159
|
-
.cmp-icon {
|
|
160
|
-
padding: 0 !important;
|
|
161
|
-
}
|
|
162
|
-
</style>
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
import { DefineComponent } from 'vue';
|
|
2
|
-
import { PopconfirmProps } from 'element-plus';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 确认按钮组件 - 基于 Element Plus Popconfirm 封装
|
|
6
|
-
* 点击后弹出确认气泡,确认后执行操作(自动防抖 500ms)
|
|
7
|
-
* 继承 Element Plus ElPopconfirm 所有属性
|
|
8
|
-
*/
|
|
9
|
-
export interface ConfrimButtonProps extends /* @vue-ignore */ Partial<PopconfirmProps> {}
|
|
10
|
-
|
|
11
|
-
export interface ConfrimButtonEmits {
|
|
12
|
-
/** 确认操作时触发(已防抖处理) */
|
|
13
|
-
(e: 'ok'): void;
|
|
14
|
-
/** 取消操作时触发 */
|
|
15
|
-
(e: 'no'): void;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
declare const ConfrimButton: DefineComponent<ConfrimButtonProps, {}, any, {}, {}, {}, {}, ConfrimButtonEmits>;
|
|
19
|
-
export default ConfrimButton;
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
* @Author: zhouyunjie 531671820@qq.com
|
|
3
|
-
* @Date: 2023-11-16 14:00:32
|
|
4
|
-
* @LastEditors: zhouyunjie 531671820@qq.com
|
|
5
|
-
* @LastEditTime: 2023-11-16 16:28:19
|
|
6
|
-
* @FilePath: /warehouse-reservation-web/src/components/ConfrimButton/index.vue
|
|
7
|
-
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
8
|
-
-->
|
|
9
|
-
<template>
|
|
10
|
-
<el-popconfirm v-bind="$attrs" @confirm="confirmHandle" @cancel="cancelHandle">
|
|
11
|
-
<template #reference>
|
|
12
|
-
<el-link underline="never" :type="$attrs.type">
|
|
13
|
-
<slot></slot>
|
|
14
|
-
</el-link>
|
|
15
|
-
</template>
|
|
16
|
-
</el-popconfirm>
|
|
17
|
-
</template>
|
|
18
|
-
|
|
19
|
-
<script setup>
|
|
20
|
-
import { debounce } from 'lodash';
|
|
21
|
-
const emits = defineEmits(['ok', 'no']);
|
|
22
|
-
const confirmHandle = debounce(() => {
|
|
23
|
-
emits('ok');
|
|
24
|
-
}, 500);
|
|
25
|
-
function cancelHandle() {
|
|
26
|
-
emits('no');
|
|
27
|
-
}
|
|
28
|
-
</script>
|
|
29
|
-
|
|
30
|
-
<style lang="scss" scoped></style>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { DefineComponent } from 'vue';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* 导入按钮组件 - 文件上传按钮
|
|
5
|
-
* 点击按钮选择文件,支持自定义文件类型
|
|
6
|
-
*/
|
|
7
|
-
export interface ImportButtonProps {
|
|
8
|
-
/** 接受的文件类型,默认 '.xlsx,.xls' */
|
|
9
|
-
accept?: string;
|
|
10
|
-
/** 按钮加载状态 */
|
|
11
|
-
loading?: boolean;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface ImportButtonEmits {
|
|
15
|
-
/** 文件选择后触发 */
|
|
16
|
-
(e: 'fileChange', file: File): void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
declare const ImportButton: DefineComponent<ImportButtonProps, {}, any, {}, {}, {}, {}, ImportButtonEmits>;
|
|
20
|
-
export default ImportButton;
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
* @Author: zhouyunjie 531671820@qq.com
|
|
3
|
-
* @Date: 2023-11-17 11:06:06
|
|
4
|
-
* @LastEditors: zhouyunjie bernie.zhou@joy-group.com
|
|
5
|
-
* @LastEditTime: 2025-07-02 19:39:47
|
|
6
|
-
* @FilePath: /warehouse-reservation-web/src/components/CmpIcon/index.vue
|
|
7
|
-
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
8
|
-
-->
|
|
9
|
-
<template>
|
|
10
|
-
<span>
|
|
11
|
-
<el-button :loading="loading" class="mgr10" @click="input.click()">
|
|
12
|
-
{{ $t('dao-ru') }}
|
|
13
|
-
</el-button>
|
|
14
|
-
<input
|
|
15
|
-
@change="fileChange"
|
|
16
|
-
ref="input"
|
|
17
|
-
:accept="accept"
|
|
18
|
-
style="position: absolute; width: 0px; height: 0px; opacity: 0"
|
|
19
|
-
type="file"
|
|
20
|
-
/>
|
|
21
|
-
</span>
|
|
22
|
-
</template>
|
|
23
|
-
|
|
24
|
-
<script setup>
|
|
25
|
-
import { ref } from 'vue';
|
|
26
|
-
|
|
27
|
-
const props = defineProps({
|
|
28
|
-
accept: {
|
|
29
|
-
type: String,
|
|
30
|
-
default: '.xlsx,.xls',
|
|
31
|
-
},
|
|
32
|
-
loading: {
|
|
33
|
-
type: Boolean,
|
|
34
|
-
default: false,
|
|
35
|
-
},
|
|
36
|
-
});
|
|
37
|
-
const emits = defineEmits(['fileChange']);
|
|
38
|
-
const input = ref(null);
|
|
39
|
-
function fileChange(e) {
|
|
40
|
-
let file = e.target.files[0];
|
|
41
|
-
emits('fileChange', file);
|
|
42
|
-
input.value.value = null;
|
|
43
|
-
}
|
|
44
|
-
</script>
|
|
45
|
-
|
|
46
|
-
<style lang="scss" scoped>
|
|
47
|
-
.cmp-icon {
|
|
48
|
-
padding: 0 !important;
|
|
49
|
-
}
|
|
50
|
-
</style>
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { DefineComponent } from 'vue';
|
|
2
|
-
import { FormProps } from 'element-plus';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* LayOutForm 组件 - 带栅格布局的表单容器
|
|
6
|
-
* 继承 Element Plus ElForm 所有属性
|
|
7
|
-
* 自动为表单项添加栅格布局,支持多列表单
|
|
8
|
-
*/
|
|
9
|
-
export interface LayOutFormProps extends /* @vue-ignore */ FormProps {
|
|
10
|
-
/** 栅格间隔,默认 20 */
|
|
11
|
-
gutter?: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export interface LayOutFormEmits {
|
|
15
|
-
/** 组件挂载完成后触发,返回表单 ref */
|
|
16
|
-
(e: 'ref', formRef: any): void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
declare const LayOutForm: DefineComponent<LayOutFormProps, {}, any, {}, {}, {}, {}, LayOutFormEmits>;
|
|
20
|
-
export default LayOutForm;
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
<script>
|
|
3
|
-
import { ElCol, ElForm, ElRow } from 'element-plus';
|
|
4
|
-
import { defineComponent, h, onMounted, ref } from 'vue';
|
|
5
|
-
|
|
6
|
-
export default defineComponent({
|
|
7
|
-
name: 'LayOutForm',
|
|
8
|
-
setup(props, { slots, attrs, emit }) {
|
|
9
|
-
const formRef = ref(null);
|
|
10
|
-
// 给表单项添加栅格 方便多列表单布局
|
|
11
|
-
const modifiedSlot = () => {
|
|
12
|
-
if (!slots.default) return null;
|
|
13
|
-
return slots
|
|
14
|
-
.default()
|
|
15
|
-
.filter((item) => item.type !== Symbol.for('v-cmt')) //过滤注释节点
|
|
16
|
-
.map((item) => (item.type === Symbol.for('v-fgt') ? item.children : item)) //如果是fragment节点则取子节点
|
|
17
|
-
.flat()
|
|
18
|
-
.map((vnode) =>
|
|
19
|
-
h(
|
|
20
|
-
ElCol,
|
|
21
|
-
{ span: vnode.props?.span || 24 },
|
|
22
|
-
{
|
|
23
|
-
default: () => {
|
|
24
|
-
return vnode;
|
|
25
|
-
},
|
|
26
|
-
},
|
|
27
|
-
),
|
|
28
|
-
);
|
|
29
|
-
};
|
|
30
|
-
onMounted(() => {
|
|
31
|
-
emit('ref', formRef.value);
|
|
32
|
-
});
|
|
33
|
-
function render() {
|
|
34
|
-
return h(ElForm, { ...attrs, ref: formRef }, () =>
|
|
35
|
-
h(ElRow, { gutter: attrs.gutter ?? 20 }, { default: () => modifiedSlot() }),
|
|
36
|
-
);
|
|
37
|
-
}
|
|
38
|
-
return render;
|
|
39
|
-
},
|
|
40
|
-
});
|
|
41
|
-
</script>
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { DefineComponent } from 'vue';
|
|
2
|
-
|
|
3
|
-
/** 搜索表单项 */
|
|
4
|
-
export interface SearchFormItem {
|
|
5
|
-
/** 字段 key */
|
|
6
|
-
key: string;
|
|
7
|
-
/** 字段名称 */
|
|
8
|
-
name: string;
|
|
9
|
-
/** 字段值 */
|
|
10
|
-
value: any;
|
|
11
|
-
/** 表单项类型 */
|
|
12
|
-
type?: 'input' | 'select' | 'date' | 'custom' | 'br';
|
|
13
|
-
/** 日期类型 */
|
|
14
|
-
dateType?: 'date' | 'daterange' | 'datetimerange';
|
|
15
|
-
/** 是否隐藏 */
|
|
16
|
-
hidden?: boolean;
|
|
17
|
-
/** 自定义渲染函数(type 为 custom 时使用) */
|
|
18
|
-
render?: () => any;
|
|
19
|
-
/** 额外配置 */
|
|
20
|
-
option?: {
|
|
21
|
-
class?: string;
|
|
22
|
-
[key: string]: any;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** 搜索表单配置 */
|
|
27
|
-
export interface SearchFormConfig {
|
|
28
|
-
/** 表单项列表 */
|
|
29
|
-
items: SearchFormItem[];
|
|
30
|
-
/** 选中的行数据(多选) */
|
|
31
|
-
selections?: any[];
|
|
32
|
-
/** 是否显示搜索栏 */
|
|
33
|
-
showSearch?: boolean;
|
|
34
|
-
/** 是否显示分页 */
|
|
35
|
-
showPage?: boolean;
|
|
36
|
-
/** 是否显示阴影 */
|
|
37
|
-
showShadow?: boolean;
|
|
38
|
-
/** 是否显示复选框 */
|
|
39
|
-
showCheckBox?: boolean;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/** 表格配置 */
|
|
43
|
-
export interface TableConfig {
|
|
44
|
-
/** 数据格式化函数 */
|
|
45
|
-
dataFormat?: (data: any[]) => any[];
|
|
46
|
-
/** 重置回调函数 */
|
|
47
|
-
reset?: () => void | Promise<void>;
|
|
48
|
-
/** 自定义配置 */
|
|
49
|
-
customConfig?: Record<string, any>;
|
|
50
|
-
/** 列配置 */
|
|
51
|
-
columnConfig?: Record<string, any>;
|
|
52
|
-
/** 表格高度 */
|
|
53
|
-
height?: number | string;
|
|
54
|
-
[key: string]: any;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* ListPage 组件 - 列表页面容器
|
|
59
|
-
* 集成搜索栏、表格、分页等功能
|
|
60
|
-
* 基于 VxeTable 实现
|
|
61
|
-
*/
|
|
62
|
-
export interface ListPageProps {
|
|
63
|
-
/** 表格唯一标识,用于本地存储列设置 */
|
|
64
|
-
id: string;
|
|
65
|
-
/** 加载状态 */
|
|
66
|
-
loading?: boolean;
|
|
67
|
-
/** 是否立即请求数据,默认 true */
|
|
68
|
-
immediate?: boolean;
|
|
69
|
-
/** 搜索表单配置 */
|
|
70
|
-
searchForm?: SearchFormConfig;
|
|
71
|
-
/** 获取列表数据的 API 函数 */
|
|
72
|
-
api?: (params: any) => Promise<{ code: number; data: { rows: any[]; totalRows: number } }>;
|
|
73
|
-
/** 静态数据源(如果提供则不使用 api) */
|
|
74
|
-
data?: any[];
|
|
75
|
-
/** 表格配置 */
|
|
76
|
-
tableConfig?: TableConfig;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/** ListPage 暴露的方法 */
|
|
80
|
-
export interface ListPageExpose {
|
|
81
|
-
/** VxeTable 实例 */
|
|
82
|
-
tableRef: any;
|
|
83
|
-
/** 刷新列表数据 */
|
|
84
|
-
getList: (params?: any) => void;
|
|
85
|
-
/** 获取当前搜索参数 */
|
|
86
|
-
getPrm: () => any;
|
|
87
|
-
/** 重新计算表格高度 */
|
|
88
|
-
calculateTableHeight: () => void;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
declare const ListPage: DefineComponent<ListPageProps, {}, any>;
|
|
92
|
-
export default ListPage;
|
|
@@ -1,258 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div :class="{ box: searchForm.showShadow }">
|
|
3
|
-
<SearchBar v-if="searchForm.showSearch" :form="searchForm" @reset="reset" @confirm="getList()">
|
|
4
|
-
<template #btn>
|
|
5
|
-
<slot name="search-bar-btn"></slot>
|
|
6
|
-
<el-button round @click="tableRef.openCustom()"><CmpIcon name="Tools" /></el-button>
|
|
7
|
-
</template>
|
|
8
|
-
</SearchBar>
|
|
9
|
-
<slot name="table-header-left"></slot>
|
|
10
|
-
<div ref="tableContentRef">
|
|
11
|
-
<VxeTable
|
|
12
|
-
:id="id"
|
|
13
|
-
ref="tableRef"
|
|
14
|
-
:loading="loading || apiLoading"
|
|
15
|
-
:data="api ? tableData : data"
|
|
16
|
-
v-bind="tableConfigCmptd"
|
|
17
|
-
:height="computedTableHeight"
|
|
18
|
-
@checkbox-change="checkboxChangeHandle"
|
|
19
|
-
@checkbox-all="checkboxChangeHandle"
|
|
20
|
-
border
|
|
21
|
-
>
|
|
22
|
-
<vxe-column
|
|
23
|
-
v-if="searchForm.showCheckBox"
|
|
24
|
-
type="checkbox"
|
|
25
|
-
:width="locale == 'zh_cn' ? 80 : 150"
|
|
26
|
-
fixed="left"
|
|
27
|
-
:title="$t('xu-hao')"
|
|
28
|
-
>
|
|
29
|
-
<template #checkbox="{ rowIndex, row, checked, disabled, indeterminate }">
|
|
30
|
-
<div class="center">
|
|
31
|
-
<!-- :checked="checked" 绑定checked值时 全选不会更新状态 -->
|
|
32
|
-
<el-checkbox
|
|
33
|
-
v-if="checked"
|
|
34
|
-
:checked="true"
|
|
35
|
-
:disabled="disabled"
|
|
36
|
-
size="default"
|
|
37
|
-
@click.stop="!disabled && toggleCheckboxEvent(row)"
|
|
38
|
-
/>
|
|
39
|
-
<el-checkbox
|
|
40
|
-
v-else
|
|
41
|
-
:disabled="disabled"
|
|
42
|
-
:checked="false"
|
|
43
|
-
size="default"
|
|
44
|
-
@click.stop="!disabled && toggleCheckboxEvent(row)"
|
|
45
|
-
/>
|
|
46
|
-
<el-text type="info" class="mgl5">{{ rowIndex + 1 }}</el-text>
|
|
47
|
-
</div>
|
|
48
|
-
</template>
|
|
49
|
-
</vxe-column>
|
|
50
|
-
<slot></slot>
|
|
51
|
-
</VxeTable>
|
|
52
|
-
</div>
|
|
53
|
-
|
|
54
|
-
<el-pagination
|
|
55
|
-
v-if="searchForm.showPage"
|
|
56
|
-
v-model:current-page="pageData.pageNo"
|
|
57
|
-
class="myPagination mgt10"
|
|
58
|
-
background
|
|
59
|
-
layout="total, sizes, prev, pager, next"
|
|
60
|
-
:total="pageData.total"
|
|
61
|
-
:page-size="pageData.pageSize"
|
|
62
|
-
:page-sizes="[10, 50, 100, 500]"
|
|
63
|
-
@current-change="handleCurrentChange"
|
|
64
|
-
@size-change="handleSizeChange"
|
|
65
|
-
/>
|
|
66
|
-
</div>
|
|
67
|
-
</template>
|
|
68
|
-
|
|
69
|
-
<script setup>
|
|
70
|
-
import SearchBar from '@/components/SearchBar/index.vue';
|
|
71
|
-
import CmpIcon from '@/components/CmpIcon/index.vue';
|
|
72
|
-
import { computed, reactive, ref, onMounted, onUnmounted, nextTick } from 'vue';
|
|
73
|
-
import { formatFieldValue } from '../SearchBar/tools';
|
|
74
|
-
import { cloneDeep, debounce } from 'lodash';
|
|
75
|
-
import { useI18n } from 'vue-i18n';
|
|
76
|
-
const { t, locale } = useI18n();
|
|
77
|
-
const props = defineProps({
|
|
78
|
-
// 表格id 本地存储列设置时使用
|
|
79
|
-
id: {
|
|
80
|
-
type: String,
|
|
81
|
-
required: true,
|
|
82
|
-
},
|
|
83
|
-
loading: {
|
|
84
|
-
type: Boolean,
|
|
85
|
-
default: false,
|
|
86
|
-
},
|
|
87
|
-
// 是否立即请求
|
|
88
|
-
immediate: {
|
|
89
|
-
type: Boolean,
|
|
90
|
-
default: true,
|
|
91
|
-
},
|
|
92
|
-
searchForm: {
|
|
93
|
-
type: Object,
|
|
94
|
-
default: () => {
|
|
95
|
-
return {
|
|
96
|
-
items: [],
|
|
97
|
-
selections: [],
|
|
98
|
-
showSearch: false,
|
|
99
|
-
showPage: false,
|
|
100
|
-
showShadow: false,
|
|
101
|
-
showCheckBox: false,
|
|
102
|
-
};
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
api: {
|
|
106
|
-
type: Function,
|
|
107
|
-
},
|
|
108
|
-
data: {
|
|
109
|
-
type: Array,
|
|
110
|
-
},
|
|
111
|
-
tableConfig: {
|
|
112
|
-
type: Object,
|
|
113
|
-
default: () => ({}),
|
|
114
|
-
},
|
|
115
|
-
});
|
|
116
|
-
const apiLoading = ref(false);
|
|
117
|
-
const tableData = ref([]);
|
|
118
|
-
const resetData = cloneDeep(props.searchForm.items);
|
|
119
|
-
const pageData = reactive({
|
|
120
|
-
pageNo: 1,
|
|
121
|
-
pageSize: 10,
|
|
122
|
-
total: 0,
|
|
123
|
-
});
|
|
124
|
-
const tableContentRef = ref(null);
|
|
125
|
-
const tableHeight = ref(450);
|
|
126
|
-
// 计算表格高度
|
|
127
|
-
const calculateTableHeight = debounce(() => {
|
|
128
|
-
nextTick(() => {
|
|
129
|
-
if (!tableContentRef.value) return;
|
|
130
|
-
const container = tableContentRef.value;
|
|
131
|
-
const containerRect = container.getBoundingClientRect();
|
|
132
|
-
const windowHeight = window.innerHeight;
|
|
133
|
-
// 计算容器顶部到视窗顶部的距离
|
|
134
|
-
const containerTop = containerRect.top;
|
|
135
|
-
const bottomReserved = 100;
|
|
136
|
-
|
|
137
|
-
// 计算表格可用高度
|
|
138
|
-
const availableHeight = windowHeight - containerTop - bottomReserved;
|
|
139
|
-
|
|
140
|
-
// 设置最小高度
|
|
141
|
-
const minHeight = 200;
|
|
142
|
-
tableHeight.value = Math.max(availableHeight, minHeight);
|
|
143
|
-
});
|
|
144
|
-
}, 100);
|
|
145
|
-
|
|
146
|
-
// 监听窗口大小变化
|
|
147
|
-
const handleResize = () => {
|
|
148
|
-
calculateTableHeight();
|
|
149
|
-
};
|
|
150
|
-
|
|
151
|
-
onMounted(() => {
|
|
152
|
-
calculateTableHeight();
|
|
153
|
-
window.addEventListener('resize', handleResize);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
onUnmounted(() => {
|
|
157
|
-
window.removeEventListener('resize', handleResize);
|
|
158
|
-
});
|
|
159
|
-
// 计算表格高度,优先使用配置的高度,否则使用自适应高度
|
|
160
|
-
const computedTableHeight = computed(() => {
|
|
161
|
-
return tableConfigCmptd.value.height || tableHeight.value;
|
|
162
|
-
});
|
|
163
|
-
async function getListHandle(prm) {
|
|
164
|
-
const { dataFormat } = props.tableConfig;
|
|
165
|
-
apiLoading.value = true;
|
|
166
|
-
const { code, data = {} } = await props.api(prm).finally(() => {
|
|
167
|
-
apiLoading.value = false;
|
|
168
|
-
props.searchForm.selections = [];
|
|
169
|
-
});
|
|
170
|
-
if (code == 200) {
|
|
171
|
-
let resData = data.rows || [];
|
|
172
|
-
tableData.value = dataFormat ? dataFormat(resData) : resData;
|
|
173
|
-
pageData.total = data.totalRows || 0;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
function getList(prm = {}) {
|
|
178
|
-
getListHandle({ ...pageData, ...formatFieldValue(props.searchForm.items), ...prm });
|
|
179
|
-
}
|
|
180
|
-
async function reset() {
|
|
181
|
-
const { reset } = props.tableConfig;
|
|
182
|
-
props.searchForm.items.forEach((item) => {
|
|
183
|
-
const resetItem = resetData.find((item2) => item2.key == item.key);
|
|
184
|
-
if (resetItem) {
|
|
185
|
-
item.value = resetItem.value;
|
|
186
|
-
}
|
|
187
|
-
});
|
|
188
|
-
reset && (await reset());
|
|
189
|
-
getList();
|
|
190
|
-
}
|
|
191
|
-
if (props.api && props.immediate) {
|
|
192
|
-
getList();
|
|
193
|
-
}
|
|
194
|
-
function getPrm() {
|
|
195
|
-
return { ...pageData, ...formatFieldValue(props.searchForm.items) };
|
|
196
|
-
}
|
|
197
|
-
const tableRef = ref(null);
|
|
198
|
-
defineExpose({ tableRef, getList, getPrm, calculateTableHeight });
|
|
199
|
-
|
|
200
|
-
const customConfig = reactive({
|
|
201
|
-
storage: true,
|
|
202
|
-
});
|
|
203
|
-
const columnConfig = reactive({
|
|
204
|
-
drag: true,
|
|
205
|
-
resizable: true,
|
|
206
|
-
maxFixedSize: 0,
|
|
207
|
-
});
|
|
208
|
-
const tableConfigCmptd = computed(() => {
|
|
209
|
-
const {
|
|
210
|
-
customConfig: prppCustomConfig,
|
|
211
|
-
columnConfig: propColumnConfig,
|
|
212
|
-
...other
|
|
213
|
-
} = props.tableConfig;
|
|
214
|
-
return {
|
|
215
|
-
headerAlign: 'left',
|
|
216
|
-
align: 'center',
|
|
217
|
-
customConfig: { ...customConfig, ...prppCustomConfig },
|
|
218
|
-
'column-config': { ...columnConfig, ...propColumnConfig },
|
|
219
|
-
// 列过多时 自动宽度 虚拟滚动会闪屏 关闭虚拟滚动
|
|
220
|
-
'virtual-x-config': {
|
|
221
|
-
enabled: false,
|
|
222
|
-
scrollToLeftOnChange: true,
|
|
223
|
-
},
|
|
224
|
-
// 纵向虚拟滚动 大于100条开启
|
|
225
|
-
'virtual-y-config': { enabled: true, gt: 100 },
|
|
226
|
-
...other,
|
|
227
|
-
};
|
|
228
|
-
});
|
|
229
|
-
// 分页相关:监听页码切换事件
|
|
230
|
-
const handleCurrentChange = (val) => {
|
|
231
|
-
pageData.pageNo = val;
|
|
232
|
-
getListHandle({ ...pageData, ...formatFieldValue(props.searchForm.items) });
|
|
233
|
-
};
|
|
234
|
-
// 分页相关:监听单页显示数量切换事件
|
|
235
|
-
const handleSizeChange = (val) => {
|
|
236
|
-
pageData.pageSize = val;
|
|
237
|
-
pageData.pageNo = 1;
|
|
238
|
-
getListHandle({ ...pageData, ...formatFieldValue(props.searchForm.items) });
|
|
239
|
-
};
|
|
240
|
-
function checkboxChangeHandle() {
|
|
241
|
-
const sles = tableRef.value.getCheckboxRecords();
|
|
242
|
-
props.searchForm.selections = sles;
|
|
243
|
-
}
|
|
244
|
-
const toggleCheckboxEvent = (row) => {
|
|
245
|
-
const $table = tableRef.value;
|
|
246
|
-
if ($table) {
|
|
247
|
-
$table.toggleCheckboxRow(row);
|
|
248
|
-
const sles = tableRef.value.getCheckboxRecords();
|
|
249
|
-
props.searchForm.selections = sles;
|
|
250
|
-
}
|
|
251
|
-
};
|
|
252
|
-
</script>
|
|
253
|
-
|
|
254
|
-
<style lang="scss" scoped>
|
|
255
|
-
:deep(.vxe-table-custom-wrapper.placement--top-right.is--active) {
|
|
256
|
-
max-height: 400px !important;
|
|
257
|
-
}
|
|
258
|
-
</style>
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import { DefineComponent } from 'vue';
|
|
2
|
-
|
|
3
|
-
/** 搜索表单项 */
|
|
4
|
-
export interface SearchBarFormItem {
|
|
5
|
-
/** 字段 key */
|
|
6
|
-
key: string;
|
|
7
|
-
/** 字段名称 */
|
|
8
|
-
name: string;
|
|
9
|
-
/** 字段值 */
|
|
10
|
-
value: any;
|
|
11
|
-
/** 表单项类型 */
|
|
12
|
-
type?: 'input' | 'select' | 'date' | 'custom' | 'br';
|
|
13
|
-
/** 日期类型 */
|
|
14
|
-
dateType?: 'date' | 'daterange' | 'datetimerange';
|
|
15
|
-
/** 是否隐藏 */
|
|
16
|
-
hidden?: boolean;
|
|
17
|
-
/** 自定义渲染函数(type 为 custom 时使用) */
|
|
18
|
-
render?: () => any;
|
|
19
|
-
/** 额外配置 */
|
|
20
|
-
option?: {
|
|
21
|
-
class?: string;
|
|
22
|
-
[key: string]: any;
|
|
23
|
-
};
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** 搜索表单配置 */
|
|
27
|
-
export interface SearchBarForm {
|
|
28
|
-
/** 表单项列表 */
|
|
29
|
-
items: SearchBarFormItem[];
|
|
30
|
-
/** 表单数据对象 */
|
|
31
|
-
data?: Record<string, any>;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* SearchBar 组件 - 搜索栏
|
|
36
|
-
* 支持多种表单项类型,可折叠展开
|
|
37
|
-
*/
|
|
38
|
-
export interface SearchBarProps {
|
|
39
|
-
/** 搜索表单配置 */
|
|
40
|
-
form: SearchBarForm;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
export interface SearchBarEmits {
|
|
44
|
-
/** 筛选按钮点击时触发 */
|
|
45
|
-
(e: 'confirm'): void;
|
|
46
|
-
/** 重置按钮点击时触发 */
|
|
47
|
-
(e: 'reset'): void;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
declare const SearchBar: DefineComponent<SearchBarProps, {}, any, {}, {}, {}, {}, SearchBarEmits>;
|
|
51
|
-
export default SearchBar;
|
|
@@ -1,112 +0,0 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="searchBar-container mgt10 mgb10" @keydown.enter="onSearch">
|
|
3
|
-
<!-- 搜索条件 -->
|
|
4
|
-
<div class="searchBar" :style="{ height: open ? 'auto' : '42px' }">
|
|
5
|
-
<el-form inline :model="form.data">
|
|
6
|
-
<template v-for="item in form.items.filter((item) => !item.hidden)" :key="item.key">
|
|
7
|
-
<br v-if="item.type == 'br'" />
|
|
8
|
-
<el-form-item v-else :label="item.name">
|
|
9
|
-
<el-input
|
|
10
|
-
v-if="item.type === 'input'"
|
|
11
|
-
v-model="item.value"
|
|
12
|
-
:class="item.option?.class || 'w150'"
|
|
13
|
-
:placeholder="$t('qing-shu-ru')"
|
|
14
|
-
clearable
|
|
15
|
-
/>
|
|
16
|
-
<component v-else-if="item.type === 'custom'" :is="item.render" />
|
|
17
|
-
<CmpDictionary
|
|
18
|
-
v-if="item.type === 'select'"
|
|
19
|
-
:class="item.option?.class || 'w150'"
|
|
20
|
-
v-model="item.value"
|
|
21
|
-
v-bind="item.option"
|
|
22
|
-
@change="onSearch"
|
|
23
|
-
@clear="onSearch"
|
|
24
|
-
/>
|
|
25
|
-
<el-date-picker
|
|
26
|
-
v-if="item.type === 'date'"
|
|
27
|
-
:class="{ datetimerange: 'w300', daterange: 'w200' }[item.dateType] || 'w100'"
|
|
28
|
-
v-model="item.value"
|
|
29
|
-
:value-format="
|
|
30
|
-
{ datetimerange: 'YYYY-MM-DD HH:mm:ss', daterange: 'YYYY-MM-DD' }[item.dateType] ||
|
|
31
|
-
'YYYY-MM-DD'
|
|
32
|
-
"
|
|
33
|
-
:type="item.dateType || 'date'"
|
|
34
|
-
@focus="currDatePicker = item"
|
|
35
|
-
@change="onDateChange(item)"
|
|
36
|
-
:placeholder="$t('qing-xuan-ze')"
|
|
37
|
-
:start-placeholder="$t('kai-shi-shi-jian')"
|
|
38
|
-
:end-placeholder="$t('jie-shu-shi-jian')"
|
|
39
|
-
/>
|
|
40
|
-
</el-form-item>
|
|
41
|
-
</template>
|
|
42
|
-
</el-form>
|
|
43
|
-
</div>
|
|
44
|
-
<div class="btns">
|
|
45
|
-
<div class="left">
|
|
46
|
-
<slot name="table-header-left"></slot>
|
|
47
|
-
</div>
|
|
48
|
-
<div class="right">
|
|
49
|
-
<el-link underline="never" class="mgr10" @click="open = !open">
|
|
50
|
-
<cmpIcon :name="open ? `ArrowUp` : `ArrowDown`" /> {{ openText }}
|
|
51
|
-
</el-link>
|
|
52
|
-
<el-button type="primary" @click="onSearch">{{ $t('shai-xuan') }}</el-button>
|
|
53
|
-
<el-button type="primary" @click="onReset">{{ $t('chong-zhi') }}</el-button>
|
|
54
|
-
<!-- 插槽填入自定义按钮 -->
|
|
55
|
-
<slot name="btn"></slot>
|
|
56
|
-
</div>
|
|
57
|
-
</div>
|
|
58
|
-
</div>
|
|
59
|
-
</template>
|
|
60
|
-
|
|
61
|
-
<script setup>
|
|
62
|
-
import { computed, defineComponent, ref, toValue } from 'vue';
|
|
63
|
-
import cmpIcon from '@/components/CmpIcon/index.vue';
|
|
64
|
-
import CmpDictionary from '@/components/CmpDictionary/index.vue';
|
|
65
|
-
import { useI18n } from 'vue-i18n';
|
|
66
|
-
const props = defineProps({
|
|
67
|
-
form: {
|
|
68
|
-
type: Object,
|
|
69
|
-
required: true,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
const emits = defineEmits(['confirm', 'reset']);
|
|
73
|
-
const open = ref(true);
|
|
74
|
-
const currDatePicker = ref(null);
|
|
75
|
-
const { t } = useI18n();
|
|
76
|
-
const openText = computed(() => (open.value ? t('guan-bi') : t('zhan-kai')));
|
|
77
|
-
function onSearch() {
|
|
78
|
-
emits('confirm');
|
|
79
|
-
}
|
|
80
|
-
function onReset() {
|
|
81
|
-
emits('reset');
|
|
82
|
-
}
|
|
83
|
-
function onDateChange() {
|
|
84
|
-
emits('confirm');
|
|
85
|
-
}
|
|
86
|
-
</script>
|
|
87
|
-
|
|
88
|
-
<style lang="scss" scoped>
|
|
89
|
-
.searchBar-container {
|
|
90
|
-
.searchBar {
|
|
91
|
-
overflow: hidden;
|
|
92
|
-
transition: all 0.3s;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
.btns {
|
|
96
|
-
display: flex;
|
|
97
|
-
justify-content: space-between;
|
|
98
|
-
align-items: center;
|
|
99
|
-
// margin-top: -10px;
|
|
100
|
-
.left {
|
|
101
|
-
display: flex;
|
|
102
|
-
align-items: center;
|
|
103
|
-
:deep(.el-tabs__header) {
|
|
104
|
-
margin: 0;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
.right {
|
|
108
|
-
text-align: right;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
</style>
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @Author: zhouyunjie bernie.zhou@joy-group.com
|
|
3
|
-
* @Date: 2025-05-09 17:43:52
|
|
4
|
-
* @LastEditors: zhouyunjie bernie.zhou@joy-group.com
|
|
5
|
-
* @LastEditTime: 2025-05-19 16:58:25
|
|
6
|
-
* @FilePath: /plm-system-web/src/components/SearchBar/tools.js
|
|
7
|
-
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
8
|
-
*/
|
|
9
|
-
import { stringToArray } from "@/utils/tools/tools";
|
|
10
|
-
import { dayjs } from "element-plus";
|
|
11
|
-
|
|
12
|
-
// 处理查询表单数据
|
|
13
|
-
export function formatFieldValue(items) {
|
|
14
|
-
let resData = {};
|
|
15
|
-
items.forEach(field => {
|
|
16
|
-
const { key, type, option = {}, value, dateType, toArr } = field
|
|
17
|
-
if (type == 'input' && option?.multiple) {
|
|
18
|
-
resData[key] = stringToArray(value);
|
|
19
|
-
} else if (type == 'date') {
|
|
20
|
-
if (dateType.includes('range')) {
|
|
21
|
-
const [start, end] = key
|
|
22
|
-
let [startVal, endVal] = value || []
|
|
23
|
-
if (startVal && dateType == 'daterange') {
|
|
24
|
-
startVal = dayjs(startVal).format('YYYY-MM-DD 00:00:00')
|
|
25
|
-
endVal = dayjs(endVal).format('YYYY-MM-DD 23:59:59')
|
|
26
|
-
}
|
|
27
|
-
resData[start] = startVal;
|
|
28
|
-
resData[end] = endVal;
|
|
29
|
-
} else {
|
|
30
|
-
resData[key] = value;
|
|
31
|
-
}
|
|
32
|
-
} else if (type == 'select') {
|
|
33
|
-
resData[key] = toArr ? [value] : value;
|
|
34
|
-
} else {
|
|
35
|
-
resData[key] = value;
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
return resData
|
|
39
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* @Author: zhouyunjie bernie.zhou@joy-group.com
|
|
3
|
-
* @Date: 2025-06-11 14:43:19
|
|
4
|
-
* @LastEditors: zhouyunjie bernie.zhou@joy-group.com
|
|
5
|
-
* @LastEditTime: 2025-07-02 17:46:48
|
|
6
|
-
* @FilePath: /mdm-system-web/src/components/VxeTable/index.jsx
|
|
7
|
-
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
8
|
-
*/
|
|
9
|
-
import { VxeUI } from 'vxe-pc-ui'
|
|
10
|
-
import EnumRender from './render/EnumRender.vue'
|
|
11
|
-
import { ElInputNumber, ElText } from 'element-plus'
|
|
12
|
-
import { getValueBykey } from '@/utils/tools/tools';
|
|
13
|
-
import { useI18n } from 'vue-i18n';
|
|
14
|
-
// renderOpts, params
|
|
15
|
-
// renderOpts cellRenderer 传入的参数
|
|
16
|
-
// params 行列参数
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// 枚举值渲染器
|
|
20
|
-
VxeUI.renderer.add('Enum', {
|
|
21
|
-
// 默认显示模板
|
|
22
|
-
renderTableDefault(renderOpts, params) {
|
|
23
|
-
if ([null, undefined].includes(params.row[params.column.field])) return '-'
|
|
24
|
-
return <EnumRender renderOpts={renderOpts} params={params} />
|
|
25
|
-
}
|
|
26
|
-
})
|
|
27
|
-
// 枚举值渲染器
|
|
28
|
-
VxeUI.renderer.add('InputNumber', {
|
|
29
|
-
// 默认显示模板
|
|
30
|
-
renderTableDefault(renderOpts, params) {
|
|
31
|
-
return <ElInputNumber v-model={params.row[params.column.field]} {...renderOpts.props} step-strictly value-on-clear={0} />
|
|
32
|
-
}
|
|
33
|
-
})
|
|
34
|
-
// 真假值渲染器
|
|
35
|
-
VxeUI.renderer.add('TrueFalse', {
|
|
36
|
-
// 默认显示模板
|
|
37
|
-
renderTableDefault(renderOpts, params) {
|
|
38
|
-
return <ElText type={params.row[params.column.field] ? 'success' : 'danger'}>{params.row[params.column.field] ? '是' : '否'}</ElText>
|
|
39
|
-
}
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
// 真假值渲染器
|
|
43
|
-
VxeUI.renderer.add('I18n', {
|
|
44
|
-
// 默认显示模板
|
|
45
|
-
renderTableDefault(renderOpts, params) {
|
|
46
|
-
const { locale } = useI18n()
|
|
47
|
-
let key = locale.value == 'zh_cn' ? params.column.field : renderOpts.fieldEn
|
|
48
|
-
// 链式字符串访问
|
|
49
|
-
if (key.includes('.')) {
|
|
50
|
-
return getValueBykey(params.row, key) || params.row[params.column.field]
|
|
51
|
-
}
|
|
52
|
-
return params.row[key] || params.row[params.column.field]
|
|
53
|
-
}
|
|
54
|
-
})
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
<!--
|
|
2
|
-
* @Author: zhouyunjie bernie.zhou@joy-group.com
|
|
3
|
-
* @Date: 2025-05-15 18:26:42
|
|
4
|
-
* @LastEditors: zhouyunjie bernie.zhou@joy-group.com
|
|
5
|
-
* @LastEditTime: 2025-05-19 11:21:11
|
|
6
|
-
* @FilePath: /plm-system-web/src/components/VxeTable/render/EnumRender.vue
|
|
7
|
-
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
|
|
8
|
-
-->
|
|
9
|
-
<template>
|
|
10
|
-
<el-text v-if="renderOpts.text">
|
|
11
|
-
{{ renderOpts.enum.getName(cellValue) }}
|
|
12
|
-
</el-text>
|
|
13
|
-
<el-text v-else :type="renderOpts.enum.getTag(cellValue)">
|
|
14
|
-
{{ renderOpts.enum.getName(cellValue) }}
|
|
15
|
-
</el-text>
|
|
16
|
-
</template>
|
|
17
|
-
|
|
18
|
-
<script setup>
|
|
19
|
-
const props = defineProps({
|
|
20
|
-
renderOpts: {
|
|
21
|
-
type: Object,
|
|
22
|
-
default: () => ({
|
|
23
|
-
enum: {},
|
|
24
|
-
}),
|
|
25
|
-
},
|
|
26
|
-
params: {
|
|
27
|
-
type: Object,
|
|
28
|
-
},
|
|
29
|
-
});
|
|
30
|
-
const cellValue = props.params.row[props.params.column.field];
|
|
31
|
-
</script>
|
|
32
|
-
|
|
33
|
-
<style></style>
|