zhl-methods 2.0.0 → 2.0.2
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.
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TableView
|
|
3
|
+
:loading="state.loading"
|
|
4
|
+
:dataSource="state.dataSource"
|
|
5
|
+
:pagination="paginationConfig"
|
|
6
|
+
height="calc(100vh - 371px)"
|
|
7
|
+
>
|
|
8
|
+
<template v-for="(_, slot) in Slots" v-slot:[slot]="scope">
|
|
9
|
+
<slot :name="slot" v-bind="scope" />
|
|
10
|
+
</template>
|
|
11
|
+
</TableView>
|
|
12
|
+
</template>
|
|
13
|
+
<script setup lang="ts">
|
|
14
|
+
import { computed, onMounted, reactive, useSlots } from "vue";
|
|
15
|
+
const Slots = useSlots();
|
|
16
|
+
import TableView from "./index.vue";
|
|
17
|
+
const props = defineProps({
|
|
18
|
+
//Promise 请求方法
|
|
19
|
+
api: { type: Function, default: () => false },
|
|
20
|
+
//请求参数
|
|
21
|
+
apiParams: { type: Object, default: () => ({}) },
|
|
22
|
+
//是否开启分页
|
|
23
|
+
isPage: { type: Boolean, default: () => true },
|
|
24
|
+
//是否进入页面自动请求
|
|
25
|
+
autoRequest: { type: Boolean, default: () => true },
|
|
26
|
+
//设置自定义参数
|
|
27
|
+
checkParams: { type: Function },
|
|
28
|
+
//设置自定义返回值
|
|
29
|
+
checkReturnValue: { type: Function },
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const state = reactive({
|
|
33
|
+
loading: false,
|
|
34
|
+
pagination: {
|
|
35
|
+
current: 1,
|
|
36
|
+
pageSize: 15,
|
|
37
|
+
total: 0,
|
|
38
|
+
},
|
|
39
|
+
dataSource: [],
|
|
40
|
+
});
|
|
41
|
+
const paginationConfig = computed(() => {
|
|
42
|
+
if (props.isPage) {
|
|
43
|
+
return {
|
|
44
|
+
...state.pagination,
|
|
45
|
+
onChange: (page: number, limit: number) => pageChange(page, limit),
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
return false;
|
|
49
|
+
});
|
|
50
|
+
const pageChange = (page: number, limit: number) => {
|
|
51
|
+
state.pagination.current = page;
|
|
52
|
+
state.pagination.pageSize = limit;
|
|
53
|
+
getData();
|
|
54
|
+
};
|
|
55
|
+
const getData = async () => {
|
|
56
|
+
let params = { ...props.apiParams };
|
|
57
|
+
|
|
58
|
+
if (props.isPage) {
|
|
59
|
+
params = { ...props.apiParams, ...state.pagination };
|
|
60
|
+
}
|
|
61
|
+
if (props.checkParams) {
|
|
62
|
+
params = props.checkParams(params);
|
|
63
|
+
}
|
|
64
|
+
try {
|
|
65
|
+
state.loading = true;
|
|
66
|
+
let res = await props.api(params);
|
|
67
|
+
state.loading = false;
|
|
68
|
+
if (res.code == 200) {
|
|
69
|
+
let resData = res.data;
|
|
70
|
+
console.log(resData);
|
|
71
|
+
if (props.checkReturnValue) {
|
|
72
|
+
resData = props.checkReturnValue(res.data);
|
|
73
|
+
}
|
|
74
|
+
if (props.isPage) {
|
|
75
|
+
state.dataSource = resData.data;
|
|
76
|
+
state.pagination.total = resData.total;
|
|
77
|
+
} else {
|
|
78
|
+
state.dataSource = resData;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} catch (error) {
|
|
82
|
+
state.loading = false;
|
|
83
|
+
console.log(error);
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
const refresh = () => {
|
|
87
|
+
pageChange(1, 15);
|
|
88
|
+
};
|
|
89
|
+
const clearData = () => {
|
|
90
|
+
state.dataSource = [];
|
|
91
|
+
state.pagination.current = 1;
|
|
92
|
+
state.pagination.pageSize = 15;
|
|
93
|
+
state.pagination.total = 0;
|
|
94
|
+
};
|
|
95
|
+
onMounted(() => {
|
|
96
|
+
if (props.autoRequest && props.api) {
|
|
97
|
+
getData();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
defineExpose({
|
|
101
|
+
refresh,
|
|
102
|
+
clearData,
|
|
103
|
+
dataSource: computed(() => state.dataSource),
|
|
104
|
+
});
|
|
105
|
+
</script>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<a-table
|
|
3
|
+
:scroll="{
|
|
4
|
+
scrollToFirstRowOnChange: true,
|
|
5
|
+
y: tableHeight,
|
|
6
|
+
}"
|
|
7
|
+
:pagination="
|
|
8
|
+
props.pagination
|
|
9
|
+
? {
|
|
10
|
+
...pagination,
|
|
11
|
+
showTotal: (total: number) => `共 ${total} 条`,
|
|
12
|
+
pageSizeOptions: ['15', '30', '50', '100', '300', '500'],
|
|
13
|
+
}
|
|
14
|
+
: false
|
|
15
|
+
"
|
|
16
|
+
>
|
|
17
|
+
<template v-for="(_, slot) in Slots" v-slot:[slot]="scope">
|
|
18
|
+
<slot :name="slot" v-bind="scope" />
|
|
19
|
+
</template>
|
|
20
|
+
</a-table>
|
|
21
|
+
</template>
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { computed, useSlots } from "vue";
|
|
24
|
+
const Slots = useSlots();
|
|
25
|
+
const props = defineProps({
|
|
26
|
+
//表格高度 只能为px vh calc()
|
|
27
|
+
height: {
|
|
28
|
+
type: String,
|
|
29
|
+
},
|
|
30
|
+
pagination: {
|
|
31
|
+
type: [Object, Boolean],
|
|
32
|
+
default: () => false,
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
// 如果传高度了,就用传的高度
|
|
36
|
+
// 如果没传高度,考虑带分页和不带分页两种情况
|
|
37
|
+
const tableHeight = computed(() => {
|
|
38
|
+
if (props.height) {
|
|
39
|
+
return props.height;
|
|
40
|
+
} else {
|
|
41
|
+
if (props.pagination) {
|
|
42
|
+
return "calc(100vh - 141px)";
|
|
43
|
+
} else {
|
|
44
|
+
return "calc(100vh - 77px)";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
</script>
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
图片上传 单个 多个
|
|
3
|
+
@author zhl
|
|
4
|
+
@date 2026/03/11
|
|
5
|
+
-->
|
|
6
|
+
<template>
|
|
7
|
+
<a-upload
|
|
8
|
+
:disabled="props.disabled"
|
|
9
|
+
list-type="picture-card"
|
|
10
|
+
:file-list="showUploadList"
|
|
11
|
+
:action="requestUrl"
|
|
12
|
+
:accept="props.fileType"
|
|
13
|
+
:headers="props.headers"
|
|
14
|
+
:data="props.data"
|
|
15
|
+
:before-upload="beforeUpload"
|
|
16
|
+
:on-success="onSuccess"
|
|
17
|
+
:on-error="onError"
|
|
18
|
+
@remove="removeImg"
|
|
19
|
+
@preview="handlePreview"
|
|
20
|
+
>
|
|
21
|
+
<template v-if="!disabled">
|
|
22
|
+
<template v-if="showUploadList.length < imgLimit">
|
|
23
|
+
<LoadingOutlined v-if="state.loading" />
|
|
24
|
+
<plus-outlined v-else />
|
|
25
|
+
</template>
|
|
26
|
+
</template>
|
|
27
|
+
</a-upload>
|
|
28
|
+
<a-modal
|
|
29
|
+
:open="state.showModal"
|
|
30
|
+
title="预览"
|
|
31
|
+
:footer="null"
|
|
32
|
+
@cancel="state.showModal = false"
|
|
33
|
+
>
|
|
34
|
+
<img alt="example" style="width: 100%" :src="state.showImgUrl" />
|
|
35
|
+
</a-modal>
|
|
36
|
+
</template>
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
import { message } from "ant-design-vue";
|
|
39
|
+
import { PlusOutlined, LoadingOutlined } from "@ant-design/icons-vue";
|
|
40
|
+
import { reactive, computed } from "vue";
|
|
41
|
+
import { URL } from "@/utils/request";
|
|
42
|
+
|
|
43
|
+
const emits = defineEmits([
|
|
44
|
+
"update:modelValue",
|
|
45
|
+
"onSuccess",
|
|
46
|
+
"getImgUrl",
|
|
47
|
+
"beforeUpload",
|
|
48
|
+
]);
|
|
49
|
+
/**
|
|
50
|
+
* 传递参数
|
|
51
|
+
* @params modelValue 绑定值 单个 或 多个
|
|
52
|
+
* @params disabled 是否禁用
|
|
53
|
+
* @params requestUrl 请求地址
|
|
54
|
+
* @params headers 头部参数
|
|
55
|
+
* @params data 更多参数
|
|
56
|
+
* @params fileType 选择图片类型 默认所有格式图片
|
|
57
|
+
*/
|
|
58
|
+
const props = defineProps({
|
|
59
|
+
modelValue: {},
|
|
60
|
+
disabled: {
|
|
61
|
+
type: Boolean,
|
|
62
|
+
},
|
|
63
|
+
imgLimit: {
|
|
64
|
+
type: Number,
|
|
65
|
+
default: () => 1,
|
|
66
|
+
},
|
|
67
|
+
data: {
|
|
68
|
+
type: Object,
|
|
69
|
+
},
|
|
70
|
+
fileType: {
|
|
71
|
+
type: String,
|
|
72
|
+
default: () => "image/*",
|
|
73
|
+
},
|
|
74
|
+
headers: { type: Object },
|
|
75
|
+
requestUrl: {
|
|
76
|
+
type: String,
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
const state = reactive({
|
|
80
|
+
loading: false,
|
|
81
|
+
showImgUrl: "",
|
|
82
|
+
showModal: false,
|
|
83
|
+
});
|
|
84
|
+
const handlePreview = async (file) => {
|
|
85
|
+
state.showImgUrl = file.url;
|
|
86
|
+
state.showModal = true;
|
|
87
|
+
};
|
|
88
|
+
/**
|
|
89
|
+
* 是否显示上传列表
|
|
90
|
+
* 单图片 多图片显示
|
|
91
|
+
* @author zhl
|
|
92
|
+
* @date 2023/12/08
|
|
93
|
+
*/
|
|
94
|
+
const showUploadList = computed(() => {
|
|
95
|
+
if (typeof props.modelValue === "string") {
|
|
96
|
+
if (props.modelValue) {
|
|
97
|
+
return [{ url: props.modelValue }];
|
|
98
|
+
} else {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
} else {
|
|
102
|
+
return props.modelValue.map((item) => {
|
|
103
|
+
return { url: item };
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
/**
|
|
108
|
+
* 上传前
|
|
109
|
+
* @author zhl
|
|
110
|
+
* @date 2023/12/06
|
|
111
|
+
*/
|
|
112
|
+
const beforeUpload = (file) => {
|
|
113
|
+
state.loading = true;
|
|
114
|
+
emits("beforeUpload", file);
|
|
115
|
+
return true;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* 上传成功
|
|
119
|
+
* @author zhl
|
|
120
|
+
* @date 2023/12/06
|
|
121
|
+
*/
|
|
122
|
+
const onSuccess = (res) => {
|
|
123
|
+
state.loading = false;
|
|
124
|
+
if (res.code == 200) {
|
|
125
|
+
if (props.imgLimit > 1) {
|
|
126
|
+
// let data = JSON.parse(JSON.stringify(props.modelValue))
|
|
127
|
+
// data.push(res.data.url)
|
|
128
|
+
// emits('update:modelValue', data)
|
|
129
|
+
props.modelValue.push(res.data.url);
|
|
130
|
+
} else {
|
|
131
|
+
emits("update:modelValue", res.data.url);
|
|
132
|
+
emits("getImgUrl", res.data.url);
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
message.error(res.msg);
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
/**
|
|
139
|
+
* 上传失败
|
|
140
|
+
* @author zhl
|
|
141
|
+
* @date 2023/12/06
|
|
142
|
+
*/
|
|
143
|
+
const onError = () => {
|
|
144
|
+
state.loading = false;
|
|
145
|
+
message.error("上传失败");
|
|
146
|
+
};
|
|
147
|
+
const removeImg = (file) => {
|
|
148
|
+
if (props.imgLimit > 1) {
|
|
149
|
+
showUploadList.value.forEach((item, index) => {
|
|
150
|
+
if (item.url == file.url) {
|
|
151
|
+
showUploadList.value.splice(index, 1);
|
|
152
|
+
props.modelValue.splice(index, 1);
|
|
153
|
+
}
|
|
154
|
+
});
|
|
155
|
+
} else {
|
|
156
|
+
emits("update:modelValue", "");
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
</script>
|
|
160
|
+
<style lang="scss"></style>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
弹窗
|
|
3
|
+
@author zhl
|
|
4
|
+
@date 2026/03/11
|
|
5
|
+
-->
|
|
6
|
+
<template>
|
|
7
|
+
<vxe-modal
|
|
8
|
+
:title="props.title"
|
|
9
|
+
v-model="state.showModal"
|
|
10
|
+
:loading="state.loading"
|
|
11
|
+
maskClosable
|
|
12
|
+
escClosable
|
|
13
|
+
:height="props.height"
|
|
14
|
+
:width="props.width"
|
|
15
|
+
:showFooter="props.showFooter"
|
|
16
|
+
show-cancel-button
|
|
17
|
+
show-confirm-button
|
|
18
|
+
destroy-on-close
|
|
19
|
+
:confirm-closable="false"
|
|
20
|
+
@confirm="emits('confirm')"
|
|
21
|
+
@cancel="emits('close')"
|
|
22
|
+
@close="emits('close')"
|
|
23
|
+
>
|
|
24
|
+
<slot></slot>
|
|
25
|
+
</vxe-modal>
|
|
26
|
+
</template>
|
|
27
|
+
<script setup lang="ts">
|
|
28
|
+
import { VxeModal } from "vxe-pc-ui";
|
|
29
|
+
import { reactive } from "vue";
|
|
30
|
+
|
|
31
|
+
const emits = defineEmits(["confirm", "close"]);
|
|
32
|
+
/**
|
|
33
|
+
* @params title 弹窗显示标题
|
|
34
|
+
* @params width 弹窗宽度 字符串 100px 100 100% 50vw calc(50vw - 50px)
|
|
35
|
+
* @params height 弹窗宽度 字符串 100px 100 100% 50vh calc(50vh - 50px)
|
|
36
|
+
* @params maskClosable 是否允许点击遮罩层关闭窗口
|
|
37
|
+
* @params escClosable 是否允许按 Esc 键关闭窗口
|
|
38
|
+
* @params showFooter 是否显示底部按钮 自定义footer会直接覆盖默认footer
|
|
39
|
+
*/
|
|
40
|
+
const props = defineProps({
|
|
41
|
+
title: { type: String },
|
|
42
|
+
width: { type: String },
|
|
43
|
+
height: { type: String },
|
|
44
|
+
maskClosable: {
|
|
45
|
+
type: Boolean,
|
|
46
|
+
default: () => true,
|
|
47
|
+
},
|
|
48
|
+
escClosable: {
|
|
49
|
+
type: Boolean,
|
|
50
|
+
default: () => true,
|
|
51
|
+
},
|
|
52
|
+
showFooter: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: () => true,
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
const state = reactive({
|
|
58
|
+
loading: false,
|
|
59
|
+
showModal: false,
|
|
60
|
+
});
|
|
61
|
+
const open = () => {
|
|
62
|
+
state.showModal = true;
|
|
63
|
+
};
|
|
64
|
+
const close = () => {
|
|
65
|
+
state.showModal = false;
|
|
66
|
+
};
|
|
67
|
+
const setLoading = (loading: boolean) => {
|
|
68
|
+
state.loading = loading;
|
|
69
|
+
};
|
|
70
|
+
defineExpose({
|
|
71
|
+
open,
|
|
72
|
+
close,
|
|
73
|
+
setLoading,
|
|
74
|
+
});
|
|
75
|
+
</script>
|