mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2024-11-01 14:10:27 +08:00
添加 export 模块,添加 crud 依赖
This commit is contained in:
parent
820b9d4749
commit
450c232c0a
2
.vscode/crud.code-snippets
vendored
2
.vscode/crud.code-snippets
vendored
@ -34,7 +34,7 @@
|
|||||||
"",
|
"",
|
||||||
"<script lang=\"ts\">",
|
"<script lang=\"ts\">",
|
||||||
"import { defineComponent, inject, reactive } from \"vue\";",
|
"import { defineComponent, inject, reactive } from \"vue\";",
|
||||||
"import { CrudLoad, Upsert, Table } from \"/$/crud/types\";",
|
"import { CrudLoad, Upsert, Table } from \"cl-admin-crud-vue3/types\";",
|
||||||
"import { useRefs } from \"/@/core\";",
|
"import { useRefs } from \"/@/core\";",
|
||||||
"",
|
"",
|
||||||
"export default defineComponent({",
|
"export default defineComponent({",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "front-next",
|
"name": "front-next",
|
||||||
"version": "0.2.7",
|
"version": "0.3.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vue-tsc --noEmit --skipLibCheck && vite build",
|
"build": "vue-tsc --noEmit --skipLibCheck && vite build",
|
||||||
@ -11,6 +11,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"array.prototype.flat": "^1.2.4",
|
"array.prototype.flat": "^1.2.4",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
|
"cl-admin-crud-vue3": "^0.1.3",
|
||||||
"clipboard": "^2.0.8",
|
"clipboard": "^2.0.8",
|
||||||
"clone-deep": "^4.0.1",
|
"clone-deep": "^4.0.1",
|
||||||
"codemirror": "^5.60.0",
|
"codemirror": "^5.60.0",
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
import Crud from "cl-admin-crud-vue3";
|
||||||
|
import "cl-admin-crud-vue3/dist/index.css";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
modules: [
|
modules: [
|
||||||
// 基础模块
|
// 基础模块
|
||||||
@ -15,6 +18,7 @@ export default {
|
|||||||
// crud 模块
|
// crud 模块
|
||||||
{
|
{
|
||||||
name: "crud",
|
name: "crud",
|
||||||
|
value: Crud,
|
||||||
options: {
|
options: {
|
||||||
crud: {
|
crud: {
|
||||||
dict: {
|
dict: {
|
||||||
|
@ -63,7 +63,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, inject, onMounted, ref } from "vue";
|
import { defineComponent, inject, onMounted, ref } from "vue";
|
||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import { ContextMenu } from "/$/crud";
|
import { ContextMenu } from "cl-admin-crud-vue3";
|
||||||
import { useRefs } from "/@/core";
|
import { useRefs } from "/@/core";
|
||||||
import { deepTree, isArray, revDeepTree, isPc } from "/@/core/utils";
|
import { deepTree, isArray, revDeepTree, isPc } from "/@/core/utils";
|
||||||
|
|
||||||
|
@ -36,7 +36,7 @@ import { useStore } from "vuex";
|
|||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { last } from "/@/core/utils";
|
import { last } from "/@/core/utils";
|
||||||
import { useRefs } from "/@/core";
|
import { useRefs } from "/@/core";
|
||||||
import { ContextMenu } from "/$/crud";
|
import { ContextMenu } from "cl-admin-crud-vue3";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "cl-process",
|
name: "cl-process",
|
||||||
|
@ -42,7 +42,7 @@
|
|||||||
import { defineComponent, inject, reactive, ref } from "vue";
|
import { defineComponent, inject, reactive, ref } from "vue";
|
||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import { useRefs } from "/@/core";
|
import { useRefs } from "/@/core";
|
||||||
import { CrudLoad, Table } from "/$/crud/types";
|
import { CrudLoad, Table } from "cl-admin-crud-vue3/types";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "sys-log",
|
name: "sys-log",
|
||||||
|
@ -81,7 +81,7 @@ import { useRefs } from "/@/core";
|
|||||||
import { deepTree } from "/@/core/utils";
|
import { deepTree } from "/@/core/utils";
|
||||||
import { useRouter } from "vue-router";
|
import { useRouter } from "vue-router";
|
||||||
import { defineComponent, inject, reactive } from "vue";
|
import { defineComponent, inject, reactive } from "vue";
|
||||||
import { CrudLoad, Table, Upsert, RefreshOp } from "/$/crud/types";
|
import { CrudLoad, Table, Upsert, RefreshOp } from "cl-admin-crud-vue3/types";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "sys-menu",
|
name: "sys-menu",
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
import { ElMessageBox } from "element-plus";
|
import { ElMessageBox } from "element-plus";
|
||||||
import { defineComponent, inject, nextTick, reactive } from "vue";
|
import { defineComponent, inject, nextTick, reactive } from "vue";
|
||||||
import { useRefs } from "/@/core";
|
import { useRefs } from "/@/core";
|
||||||
import { CrudLoad, Table, Upsert } from "/$/crud/types";
|
import { CrudLoad, Table, Upsert } from "cl-admin-crud-vue3/types";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "sys-param",
|
name: "sys-param",
|
||||||
|
@ -51,7 +51,7 @@ import { ElMessage } from "element-plus";
|
|||||||
import { defineComponent, inject, reactive } from "vue";
|
import { defineComponent, inject, reactive } from "vue";
|
||||||
import { checkPerm } from "/$/base";
|
import { checkPerm } from "/$/base";
|
||||||
import { useRefs } from "/@/core";
|
import { useRefs } from "/@/core";
|
||||||
import { CrudLoad, RefreshOp, Table } from "/$/crud/types";
|
import { CrudLoad, RefreshOp, Table } from "cl-admin-crud-vue3/types";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "plugin",
|
name: "plugin",
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { CrudLoad, Table, Upsert } from "/$/crud/types";
|
import { CrudLoad, Table, Upsert } from "cl-admin-crud-vue3/types";
|
||||||
import { defineComponent, inject, reactive } from "vue";
|
import { defineComponent, inject, reactive } from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -112,7 +112,7 @@
|
|||||||
import { computed, defineComponent, inject, reactive, ref, watch } from "vue";
|
import { computed, defineComponent, inject, reactive, ref, watch } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import { useRefs } from "/@/core";
|
import { useRefs } from "/@/core";
|
||||||
import { Table, Upsert } from "/$/crud/types";
|
import { Table, Upsert } from "cl-admin-crud-vue3/types";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "sys-user",
|
name: "sys-user",
|
||||||
|
@ -57,7 +57,7 @@ import { computed, defineComponent, inject, onUnmounted, reactive, ref } from "v
|
|||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { isEmpty } from "/@/core/utils";
|
import { isEmpty } from "/@/core/utils";
|
||||||
import { ContextMenu } from "/$/crud";
|
import { ContextMenu } from "cl-admin-crud-vue3";
|
||||||
import { parseContent } from "../utils";
|
import { parseContent } from "../utils";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -1,30 +0,0 @@
|
|||||||
import { defineComponent, inject } from "vue";
|
|
||||||
import { Crud } from "../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-add-btn",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
props: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props, { slots }) {
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
return (
|
|
||||||
crud.getPermission("add") && (
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="primary"
|
|
||||||
onClick={() => {
|
|
||||||
crud.rowAdd();
|
|
||||||
}}
|
|
||||||
{...props}>
|
|
||||||
{slots.default ? slots.default() : crud.dict.label.add}
|
|
||||||
</el-button>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,30 +0,0 @@
|
|||||||
import { defineComponent, inject } from "vue";
|
|
||||||
import { Crud } from "../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-adv-btn",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
props: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props, { slots }) {
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
return (
|
|
||||||
<div class="cl-adv-btn">
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
onClick={() => {
|
|
||||||
crud.openAdvSearch();
|
|
||||||
}}
|
|
||||||
{...props}>
|
|
||||||
<i class="el-icon-search" />
|
|
||||||
{slots.default ? slots.default() : crud.dict.label.advSearch}
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,283 +0,0 @@
|
|||||||
import { defineComponent, inject, reactive, ref } from "vue";
|
|
||||||
import { useAction } from "./form/helper";
|
|
||||||
import { useForm, useRefs } from "../hooks/core";
|
|
||||||
import { cloneDeep, deepMerge } from "../utils";
|
|
||||||
import Parse from "../utils/parse";
|
|
||||||
import { renderNode } from "../utils/vnode";
|
|
||||||
import { Browser, Crud, Mitt } from "../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-adv-search",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
// 绑定值
|
|
||||||
modelValue: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 表单项
|
|
||||||
items: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
// el-drawer 参数
|
|
||||||
props: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 操作按钮 ['search', 'reset', 'clear', 'close']
|
|
||||||
opList: {
|
|
||||||
type: Array,
|
|
||||||
default: () => ["close", "search"]
|
|
||||||
},
|
|
||||||
// 打开钩子 { data, { next } }
|
|
||||||
onOpen: Function,
|
|
||||||
// 关闭钩子 { done }
|
|
||||||
onClose: Function,
|
|
||||||
// 搜索钩子 { data, { next, close } }
|
|
||||||
onSearch: Function
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["update:modelValue", "open", "opened", "close", "closed", "reset", "clear"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const { refs, setRefs } = useRefs();
|
|
||||||
const { setFormData } = useForm(props);
|
|
||||||
|
|
||||||
// 参数注入
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
const mitt = inject("mitt") as Mitt;
|
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const form = setFormData();
|
|
||||||
|
|
||||||
// 是否可见
|
|
||||||
const visible = ref<boolean>(false);
|
|
||||||
|
|
||||||
// 表单配置
|
|
||||||
const conf = reactive<any>({
|
|
||||||
items: props.items,
|
|
||||||
op: {
|
|
||||||
buttons: props.opList
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 表单动作
|
|
||||||
const {
|
|
||||||
getForm,
|
|
||||||
setForm,
|
|
||||||
setData,
|
|
||||||
setOptions,
|
|
||||||
toggleItem,
|
|
||||||
hiddenItem,
|
|
||||||
showItem,
|
|
||||||
resetFields,
|
|
||||||
clearValidate
|
|
||||||
} = useAction({ conf, form, refs });
|
|
||||||
|
|
||||||
// 打开
|
|
||||||
function open() {
|
|
||||||
conf.items.map((e: any) => {
|
|
||||||
if (form[e.prop] === undefined) {
|
|
||||||
form[e.prop] = e.value;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const next = (data: any) => {
|
|
||||||
visible.value = true;
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
deepMerge(form, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit("open", form);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (props.onOpen) {
|
|
||||||
props.onOpen(form, { next });
|
|
||||||
} else {
|
|
||||||
next(null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开动画结束
|
|
||||||
function onOpened() {
|
|
||||||
emit("opened");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭
|
|
||||||
function close() {
|
|
||||||
const done = () => {
|
|
||||||
visible.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (props.onClose) {
|
|
||||||
props.onClose(done);
|
|
||||||
} else {
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭回调
|
|
||||||
function onClose2() {
|
|
||||||
emit("close");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭动画结束
|
|
||||||
function onClosed() {
|
|
||||||
emit("closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重置数据
|
|
||||||
function reset() {
|
|
||||||
resetFields();
|
|
||||||
emit("reset");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空数据
|
|
||||||
function clear() {
|
|
||||||
for (const i in form) {
|
|
||||||
form[i] = undefined;
|
|
||||||
}
|
|
||||||
clearValidate();
|
|
||||||
emit("clear");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 搜素请求
|
|
||||||
function search() {
|
|
||||||
const params = cloneDeep(form);
|
|
||||||
|
|
||||||
const next = (params: any) => {
|
|
||||||
crud.refresh({
|
|
||||||
...params,
|
|
||||||
page: 1
|
|
||||||
});
|
|
||||||
|
|
||||||
close();
|
|
||||||
};
|
|
||||||
|
|
||||||
if (props.onSearch) {
|
|
||||||
props.onSearch(params, { next, close });
|
|
||||||
} else {
|
|
||||||
next(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 消息事件
|
|
||||||
mitt.on("crud.openAdvSearch", open);
|
|
||||||
|
|
||||||
return {
|
|
||||||
refs,
|
|
||||||
visible,
|
|
||||||
conf,
|
|
||||||
form,
|
|
||||||
setRefs,
|
|
||||||
open,
|
|
||||||
onOpened,
|
|
||||||
close,
|
|
||||||
onClose2,
|
|
||||||
onClosed,
|
|
||||||
reset,
|
|
||||||
clear,
|
|
||||||
search,
|
|
||||||
getForm,
|
|
||||||
setForm,
|
|
||||||
setData,
|
|
||||||
setOptions,
|
|
||||||
toggleItem,
|
|
||||||
hiddenItem,
|
|
||||||
showItem
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
const browser = inject("browser") as Browser;
|
|
||||||
|
|
||||||
// 渲染表单
|
|
||||||
function renderForm() {
|
|
||||||
return (
|
|
||||||
<el-form
|
|
||||||
ref={ctx.setRefs("form")}
|
|
||||||
class="cl-form"
|
|
||||||
size="small"
|
|
||||||
label-width="100px"
|
|
||||||
model={ctx.form}
|
|
||||||
{...ctx.props}>
|
|
||||||
<el-row>
|
|
||||||
{ctx.conf.items.map((e: any) => {
|
|
||||||
return (
|
|
||||||
!Parse("hidden", {
|
|
||||||
value: e.hidden,
|
|
||||||
scope: ctx.form
|
|
||||||
}) && (
|
|
||||||
<el-col span={24} {...e}>
|
|
||||||
<el-form-item {...e}>
|
|
||||||
{renderNode(e.component, {
|
|
||||||
prop: e.prop,
|
|
||||||
scope: ctx.form,
|
|
||||||
slots: ctx.$slots
|
|
||||||
})}
|
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 渲染底部
|
|
||||||
function renderFooter() {
|
|
||||||
const btns: any = {
|
|
||||||
search: "搜索",
|
|
||||||
reset: "重置",
|
|
||||||
clear: "清空",
|
|
||||||
close: "取消"
|
|
||||||
};
|
|
||||||
|
|
||||||
return ctx.opList.map((e: any) => {
|
|
||||||
if (btns[e]) {
|
|
||||||
return (
|
|
||||||
<el-button
|
|
||||||
{...{
|
|
||||||
size: ctx.props.size || "small",
|
|
||||||
type: e === "search" ? "primary" : null,
|
|
||||||
onClick: ctx[e]
|
|
||||||
}}>
|
|
||||||
{btns[e]}
|
|
||||||
</el-button>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return renderNode(e, {
|
|
||||||
scope: ctx.form,
|
|
||||||
slots: ctx.$slots
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div class="cl-adv-search">
|
|
||||||
<el-drawer
|
|
||||||
v-model={ctx.visible}
|
|
||||||
title="高级搜索"
|
|
||||||
direction="rtl"
|
|
||||||
size={browser.isMini ? "100%" : ctx.props.size || "30%"}
|
|
||||||
{...{
|
|
||||||
onOpened: ctx.onOpened,
|
|
||||||
onClosed: ctx.onClosed,
|
|
||||||
onClose: ctx.onClose2,
|
|
||||||
...ctx.props
|
|
||||||
}}>
|
|
||||||
<div class="cl-adv-search__container">{renderForm()}</div>
|
|
||||||
<div class="cl-adv-search__footer">{renderFooter()}</div>
|
|
||||||
</el-drawer>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,216 +0,0 @@
|
|||||||
import { defineComponent, nextTick, onMounted, reactive, ref } from "vue";
|
|
||||||
import type { PropType } from "vue";
|
|
||||||
import { useRefs } from "../../hooks/core";
|
|
||||||
import { contains } from "../../utils";
|
|
||||||
import { ContextMenuItem, ContextMenuOptions } from "../../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-context-menu",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
visible: Boolean,
|
|
||||||
options: {
|
|
||||||
type: Object as PropType<ContextMenuOptions>,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
event: Object
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const { refs, setRefs }: any = useRefs();
|
|
||||||
|
|
||||||
// 菜单是否可见
|
|
||||||
const visible2 = ref<boolean>(props.visible);
|
|
||||||
|
|
||||||
// 按钮列表
|
|
||||||
const list = ref<Array<ContextMenuItem>>([]);
|
|
||||||
|
|
||||||
// 菜单样式
|
|
||||||
const style = reactive<any>({
|
|
||||||
left: 0,
|
|
||||||
top: 0
|
|
||||||
});
|
|
||||||
|
|
||||||
// 选中值
|
|
||||||
const ids = ref<string>("");
|
|
||||||
|
|
||||||
// 阻止默认事件
|
|
||||||
function stopDefault(e: any) {
|
|
||||||
if (e.preventDefault) {
|
|
||||||
e.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.stopPropagation) {
|
|
||||||
e.stopPropagation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析列表
|
|
||||||
function parseList(list: Array<ContextMenuItem>) {
|
|
||||||
const deep = (list: any[]) => {
|
|
||||||
list.forEach((e: any) => {
|
|
||||||
e.showChildren = false;
|
|
||||||
|
|
||||||
if (e.children) {
|
|
||||||
deep(e.children);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
deep(list);
|
|
||||||
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭菜单
|
|
||||||
function close() {
|
|
||||||
visible2.value = false;
|
|
||||||
ids.value = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开菜单
|
|
||||||
function open(event: any, options?: ContextMenuOptions) {
|
|
||||||
let left: number = event.pageX;
|
|
||||||
let top: number = event.pageY;
|
|
||||||
|
|
||||||
if (!options) {
|
|
||||||
options = {};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options.list) {
|
|
||||||
list.value = parseList(options.list);
|
|
||||||
}
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
const { clientHeight: h1, clientWidth: w1 } = document.body;
|
|
||||||
const { clientHeight: h2, clientWidth: w2 } = refs.value["context-menu"];
|
|
||||||
|
|
||||||
if (top + h2 > h1) {
|
|
||||||
top = h1 - h2 - 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (left + w2 > w1) {
|
|
||||||
left = w1 - w2 - 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
style.left = left + "px";
|
|
||||||
style.top = top + "px";
|
|
||||||
});
|
|
||||||
|
|
||||||
// 阻止默认事件
|
|
||||||
stopDefault(event);
|
|
||||||
|
|
||||||
// 显示菜单
|
|
||||||
visible2.value = true;
|
|
||||||
|
|
||||||
return {
|
|
||||||
close
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 行点击
|
|
||||||
function rowClick(e: any, id: string) {
|
|
||||||
ids.value = id;
|
|
||||||
|
|
||||||
if (e.disabled) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.callback) {
|
|
||||||
return e.callback(e, () => {
|
|
||||||
close();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.children) {
|
|
||||||
e.showChildren = !e.showChildren;
|
|
||||||
} else {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(function () {
|
|
||||||
if (visible2.value) {
|
|
||||||
// 添加到 body 下
|
|
||||||
document.body.appendChild(refs.value["context-menu"]);
|
|
||||||
// 关闭事件
|
|
||||||
(document.documentElement || document.body).addEventListener("mousedown", (e) => {
|
|
||||||
const el = refs.value["context-menu"];
|
|
||||||
if (!contains(el, e.target) && el != e.target) {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 默认打开
|
|
||||||
open(props.event, props.options);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
refs,
|
|
||||||
visible2,
|
|
||||||
ids,
|
|
||||||
style,
|
|
||||||
list,
|
|
||||||
setRefs,
|
|
||||||
open,
|
|
||||||
close,
|
|
||||||
rowClick,
|
|
||||||
stopDefault
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
function deep(list: any[], pId: string, level: number) {
|
|
||||||
return (
|
|
||||||
<div class={["cl-context-menu__box", level > 1 && "is-append"]}>
|
|
||||||
{list
|
|
||||||
.filter((e) => !e.hidden)
|
|
||||||
.map((e, i) => {
|
|
||||||
const id = `${pId}-${i}`;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
class={{
|
|
||||||
"is-active": ctx.ids.includes(id),
|
|
||||||
"is-ellipsis": e.ellipsis,
|
|
||||||
"is-disabled": e.disabled
|
|
||||||
}}>
|
|
||||||
{/* 前缀图标 */}
|
|
||||||
{e["prefix-icon"] && <i class={e["prefix-icon"]}></i>}
|
|
||||||
|
|
||||||
{/* 标题 */}
|
|
||||||
<span
|
|
||||||
onClick={() => {
|
|
||||||
ctx.rowClick(e, id);
|
|
||||||
}}>
|
|
||||||
{e.label}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{/* 后缀图标 */}
|
|
||||||
{e["suffix-icon"] && <i class={e["suffix-icon"]}></i>}
|
|
||||||
|
|
||||||
{/* 子集*/}
|
|
||||||
{e.children &&
|
|
||||||
e.showChildren &&
|
|
||||||
deep(e.children, id, level + 1)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
ctx.visible2 && (
|
|
||||||
<div
|
|
||||||
class="cl-context-menu"
|
|
||||||
ref={ctx.setRefs("context-menu")}
|
|
||||||
style={ctx.style}
|
|
||||||
onContextmenu={ctx.stopDefault}>
|
|
||||||
{ctx.$slots.default ? ctx.$slots.default() : deep(ctx.list, "0", 1)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,16 +0,0 @@
|
|||||||
import { h, render } from "vue";
|
|
||||||
import ContextMenuConstructor from "./context-menu";
|
|
||||||
|
|
||||||
class ContextMenu {
|
|
||||||
open(event: any, options: any) {
|
|
||||||
const vm: any = h(ContextMenuConstructor, {
|
|
||||||
visible: true,
|
|
||||||
event,
|
|
||||||
options
|
|
||||||
});
|
|
||||||
|
|
||||||
render(vm, document.createElement("div"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default new ContextMenu();
|
|
@ -1,53 +0,0 @@
|
|||||||
import { merge } from "merge";
|
|
||||||
import { deepMerge, isFunction } from "../../utils";
|
|
||||||
|
|
||||||
export const bootstap = (crud: any, { fn }: any) => {
|
|
||||||
const { params, permission, service, refresh, id } = crud || {};
|
|
||||||
|
|
||||||
const app = {
|
|
||||||
refresh(d: any) {
|
|
||||||
return isFunction(d) ? d(params, refresh) : refresh(d);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function ctx(data: any) {
|
|
||||||
deepMerge(crud, data);
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.id = id;
|
|
||||||
|
|
||||||
ctx.service = function (s: any) {
|
|
||||||
if (s) {
|
|
||||||
Object.assign(crud.service, s);
|
|
||||||
crud.service.__proto__ = s.__proto__;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fn.permission) {
|
|
||||||
merge(permission, fn.permission({ permission, service, refresh }));
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.permission = function (d: any) {
|
|
||||||
if (isFunction(d)) {
|
|
||||||
merge(permission, d({ service, permission }));
|
|
||||||
} else {
|
|
||||||
merge(permission, d);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.set = (key: string, value: any) => {
|
|
||||||
deepMerge(crud[key], value);
|
|
||||||
|
|
||||||
return ctx;
|
|
||||||
};
|
|
||||||
|
|
||||||
ctx.done = function () {};
|
|
||||||
|
|
||||||
return { ctx, app };
|
|
||||||
};
|
|
@ -1,205 +0,0 @@
|
|||||||
import { ElMessageBox, ElMessage } from "element-plus";
|
|
||||||
import { isArray, isObject, isString } from "../../utils";
|
|
||||||
import { ServiceName } from "../../types";
|
|
||||||
|
|
||||||
export function useRequest({ mitt, props, crud }: any) {
|
|
||||||
// 刷新随机值,避免脏数据
|
|
||||||
let refreshRd = 0;
|
|
||||||
|
|
||||||
// 获取权限
|
|
||||||
function getPermission(key: ServiceName): boolean {
|
|
||||||
switch (key) {
|
|
||||||
case "update":
|
|
||||||
return Boolean(crud.permission["update"]);
|
|
||||||
default:
|
|
||||||
return Boolean(crud.permission[key]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 根据字典替换请求参数
|
|
||||||
function paramsReplace(params: any) {
|
|
||||||
const { pagination, search, sort } = crud.dict;
|
|
||||||
const a: any = { ...params };
|
|
||||||
const b: any = { ...pagination, ...search, ...sort };
|
|
||||||
|
|
||||||
for (const i in b) {
|
|
||||||
if (a[i]) {
|
|
||||||
if (i != b[i]) {
|
|
||||||
a[`_${b[i]}`] = a[i];
|
|
||||||
|
|
||||||
delete a[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const i in a) {
|
|
||||||
if (i[0] === "_") {
|
|
||||||
a[i.substr(1)] = a[i];
|
|
||||||
|
|
||||||
delete a[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 刷新请求
|
|
||||||
function refresh(newParams?: any) {
|
|
||||||
// 合并请求参数
|
|
||||||
const reqParams = paramsReplace(Object.assign(crud.params, newParams));
|
|
||||||
|
|
||||||
// Loading
|
|
||||||
crud.loading = true;
|
|
||||||
|
|
||||||
// 预防脏数据
|
|
||||||
const rd = (refreshRd = Math.random());
|
|
||||||
|
|
||||||
// 完成事件
|
|
||||||
const done = () => {
|
|
||||||
crud.loading = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// 渲染
|
|
||||||
const render = (list: Array<any>, pagination?: any) => {
|
|
||||||
mitt.emit("crud.refresh", { list, pagination });
|
|
||||||
done();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 请求执行
|
|
||||||
const next = (params: any) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
const reqName = crud.dict.api.page;
|
|
||||||
|
|
||||||
if (!crud.service[reqName]) {
|
|
||||||
done();
|
|
||||||
return reject(`Request function '${reqName}' is not fount`);
|
|
||||||
}
|
|
||||||
|
|
||||||
crud.service[reqName](params)
|
|
||||||
.then((res: any) => {
|
|
||||||
if (rd != refreshRd) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isString(res)) {
|
|
||||||
return reject("Response error");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isArray(res)) {
|
|
||||||
render(res);
|
|
||||||
} else if (isObject(res)) {
|
|
||||||
render(res.list, res.pagination);
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(res);
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch((err: string) => {
|
|
||||||
ElMessage.error(err);
|
|
||||||
reject(err);
|
|
||||||
done();
|
|
||||||
console.error(err);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (props.onRefresh) {
|
|
||||||
return props.onRefresh(reqParams, { next, done, render });
|
|
||||||
} else {
|
|
||||||
return next(reqParams);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除请求
|
|
||||||
function rowDelete(...selection: Array<any>) {
|
|
||||||
// 获取请求方法
|
|
||||||
const reqName = crud.dict.api.delete;
|
|
||||||
|
|
||||||
const params = {
|
|
||||||
ids: selection.map((e) => e.id)
|
|
||||||
};
|
|
||||||
|
|
||||||
const next = (params: any) => {
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
ElMessageBox.confirm(`此操作将永久删除选中数据,是否继续?`, "提示", {
|
|
||||||
type: "warning"
|
|
||||||
})
|
|
||||||
.then((res: any) => {
|
|
||||||
if (res === "confirm") {
|
|
||||||
// 验证方法
|
|
||||||
if (!crud.service[reqName]) {
|
|
||||||
return reject(`Request function '${reqName}' is not fount`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送请求
|
|
||||||
crud.service[reqName](params)
|
|
||||||
.then((res: any) => {
|
|
||||||
ElMessage.success(`删除成功`);
|
|
||||||
refresh();
|
|
||||||
resolve(res);
|
|
||||||
})
|
|
||||||
.catch((err: string) => {
|
|
||||||
ElMessage.error(err);
|
|
||||||
reject(err);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => null);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
if (props.onDelete) {
|
|
||||||
props.onDelete(selection, { next });
|
|
||||||
} else {
|
|
||||||
next(params);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
rowDelete,
|
|
||||||
refresh,
|
|
||||||
getPermission,
|
|
||||||
paramsReplace
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useMitt({ mitt }: any) {
|
|
||||||
// 打开新增
|
|
||||||
function rowAdd() {
|
|
||||||
mitt.emit("crud.add");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开编辑
|
|
||||||
function rowEdit(data: any) {
|
|
||||||
mitt.emit("crud.edit", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开追加
|
|
||||||
function rowAppend(data: any) {
|
|
||||||
mitt.emit("crud.append", data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭新增、编辑弹窗
|
|
||||||
function rowClose() {
|
|
||||||
mitt.emit("crud.close");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开高级搜索
|
|
||||||
function openAdvSearch() {
|
|
||||||
mitt.emit("crud.openAdvSearch");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭高级搜索
|
|
||||||
function closeAdvSearch() {
|
|
||||||
mitt.emit("crud.closeAdvSearch");
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
rowAdd,
|
|
||||||
rowEdit,
|
|
||||||
rowAppend,
|
|
||||||
rowClose,
|
|
||||||
openAdvSearch,
|
|
||||||
closeAdvSearch
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,120 +0,0 @@
|
|||||||
import {
|
|
||||||
defineComponent,
|
|
||||||
getCurrentInstance,
|
|
||||||
inject,
|
|
||||||
onMounted,
|
|
||||||
provide,
|
|
||||||
reactive,
|
|
||||||
ref
|
|
||||||
} from "vue";
|
|
||||||
import { useMitt, useRequest } from "./helper";
|
|
||||||
import { bootstap } from "./app";
|
|
||||||
import Mitt from "../../utils/mitt";
|
|
||||||
import { deepMerge } from "../../utils";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-crud",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
name: String,
|
|
||||||
border: Boolean,
|
|
||||||
onDelete: Function,
|
|
||||||
onRefresh: Function
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["load"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const ctx = getCurrentInstance();
|
|
||||||
|
|
||||||
// 组件间通讯
|
|
||||||
const mitt = new Mitt(ctx?.uid);
|
|
||||||
|
|
||||||
// 配置
|
|
||||||
const crud = reactive<any>({
|
|
||||||
dict: {
|
|
||||||
api: {
|
|
||||||
list: "list",
|
|
||||||
add: "add",
|
|
||||||
update: "update",
|
|
||||||
delete: "delete",
|
|
||||||
info: "info",
|
|
||||||
page: "page"
|
|
||||||
},
|
|
||||||
pagination: {
|
|
||||||
page: "page",
|
|
||||||
size: "size"
|
|
||||||
},
|
|
||||||
search: {
|
|
||||||
keyWord: "keyWord",
|
|
||||||
query: "query"
|
|
||||||
},
|
|
||||||
sort: {
|
|
||||||
order: "order",
|
|
||||||
prop: "prop"
|
|
||||||
},
|
|
||||||
label: {
|
|
||||||
add: "新增",
|
|
||||||
delete: "删除",
|
|
||||||
multiDelete: "删除",
|
|
||||||
update: "编辑",
|
|
||||||
refresh: "刷新",
|
|
||||||
advSearch: "高级搜索",
|
|
||||||
saveButtonText: "保存",
|
|
||||||
closeButtonText: "关闭"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selection: [],
|
|
||||||
table: {
|
|
||||||
"context-menu": true
|
|
||||||
},
|
|
||||||
crudRef: ref<any>({}),
|
|
||||||
service: {},
|
|
||||||
loading: false,
|
|
||||||
params: {
|
|
||||||
page: 1,
|
|
||||||
size: 20
|
|
||||||
},
|
|
||||||
permission: {
|
|
||||||
update: true,
|
|
||||||
page: true,
|
|
||||||
info: true,
|
|
||||||
list: true,
|
|
||||||
add: true,
|
|
||||||
delete: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 集合
|
|
||||||
Object.assign(crud, useMitt({ mitt }), useRequest({ mitt, props, crud }));
|
|
||||||
|
|
||||||
// 临时处理方法
|
|
||||||
const fn: any = {
|
|
||||||
permission: null
|
|
||||||
};
|
|
||||||
|
|
||||||
// 提供
|
|
||||||
provide("crud", crud);
|
|
||||||
provide("mitt", mitt);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
// 加载完成回调
|
|
||||||
emit("load", bootstap(deepMerge(crud, inject("__crud")), { fn }));
|
|
||||||
|
|
||||||
// 监听窗口大小改变事件
|
|
||||||
window.addEventListener("resize", () => {
|
|
||||||
mitt.emit("crud.resize");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return crud;
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
return (
|
|
||||||
<div class={["cl-crud", { "is-border": ctx.border }]} ref="crudRef">
|
|
||||||
{ctx.$slots.default && ctx.$slots.default()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,139 +0,0 @@
|
|||||||
import { getCurrentInstance, nextTick } from "vue";
|
|
||||||
|
|
||||||
export function useDialog({ props, isFullscreen }: any) {
|
|
||||||
const ctx = getCurrentInstance();
|
|
||||||
|
|
||||||
// 设置对话框样式、拖动
|
|
||||||
const setDialog = () => {
|
|
||||||
const { top = "15vh" } = props.props;
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
// 获取元素
|
|
||||||
const dlg: any = document.querySelector(`.cl-dialog--${ctx?.uid}`);
|
|
||||||
const hdr: any = dlg ? dlg.querySelector(".el-dialog__header") : null;
|
|
||||||
|
|
||||||
// 设置对话框
|
|
||||||
if (dlg) {
|
|
||||||
dlg.style.left = 0;
|
|
||||||
|
|
||||||
if (isFullscreen.value) {
|
|
||||||
dlg.style.top = 0;
|
|
||||||
dlg.style.marginBottom = 0;
|
|
||||||
} else {
|
|
||||||
dlg.style.marginBottom = "160px";
|
|
||||||
dlg.style.top = top;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置光标
|
|
||||||
hdr.style.cursor = isFullscreen.value ? "text" : "move";
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置头部
|
|
||||||
if (hdr) {
|
|
||||||
hdr.onmousedown = (e: any) => {
|
|
||||||
// 可视区域大小
|
|
||||||
const { clientWidth, clientHeight } = document.documentElement || document.body;
|
|
||||||
|
|
||||||
// Try drag
|
|
||||||
const isDrag = (() => {
|
|
||||||
if (isFullscreen.value) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否能拖动
|
|
||||||
if (!props.drag) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine height of the box is too large
|
|
||||||
let marginTop = 0;
|
|
||||||
|
|
||||||
if (["vh", "%"].some((e) => top.includes(e))) {
|
|
||||||
marginTop = clientHeight * (parseInt(top) / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (top.includes("px")) {
|
|
||||||
marginTop = top;
|
|
||||||
}
|
|
||||||
|
|
||||||
return dlg.clientHeight < clientHeight - marginTop;
|
|
||||||
})();
|
|
||||||
|
|
||||||
// 设置指针状态
|
|
||||||
if (!isDrag) {
|
|
||||||
return (hdr.style.cursor = "text");
|
|
||||||
} else {
|
|
||||||
hdr.style.cursor = "move";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Distance
|
|
||||||
const dis = {
|
|
||||||
left: e.clientX - hdr.offsetLeft,
|
|
||||||
top: e.clientY - hdr.offsetTop
|
|
||||||
};
|
|
||||||
|
|
||||||
// Calc left and top of the box
|
|
||||||
const box = (() => {
|
|
||||||
const { left, top } =
|
|
||||||
dlg.currentStyle || window.getComputedStyle(dlg, null);
|
|
||||||
|
|
||||||
if (left.includes("%")) {
|
|
||||||
return {
|
|
||||||
top: +clientHeight * (+top.replace(/%/g, "") / 100),
|
|
||||||
left: +clientWidth * (+left.replace(/%/g, "") / 100)
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
return {
|
|
||||||
top: +top.replace(/\px/g, ""),
|
|
||||||
left: +left.replace(/\px/g, "")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
// Screen limit
|
|
||||||
const pad = 5;
|
|
||||||
const minLeft = -(clientWidth - dlg.clientWidth) / 2 + pad;
|
|
||||||
const maxLeft =
|
|
||||||
(dlg.clientWidth >= clientWidth / 2
|
|
||||||
? dlg.clientWidth / 2 - (dlg.clientWidth - clientWidth / 2)
|
|
||||||
: dlg.clientWidth / 2 + clientWidth / 2 - dlg.clientWidth) - pad;
|
|
||||||
|
|
||||||
const minTop = pad;
|
|
||||||
const maxTop = clientHeight - dlg.clientHeight - pad;
|
|
||||||
|
|
||||||
// Start move
|
|
||||||
document.onmousemove = function (e) {
|
|
||||||
let left = e.clientX - dis.left + box.left;
|
|
||||||
let top = e.clientY - dis.top + box.top;
|
|
||||||
|
|
||||||
if (left < minLeft) {
|
|
||||||
left = minLeft;
|
|
||||||
} else if (left >= maxLeft) {
|
|
||||||
left = maxLeft;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (top < minTop) {
|
|
||||||
top = minTop;
|
|
||||||
} else if (top >= maxTop) {
|
|
||||||
top = maxTop;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set dialog top and left
|
|
||||||
dlg.style.top = top + "px";
|
|
||||||
dlg.style.left = left + "px";
|
|
||||||
};
|
|
||||||
|
|
||||||
// Clear event
|
|
||||||
document.onmouseup = function () {
|
|
||||||
document.onmousemove = null;
|
|
||||||
document.onmouseup = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
setDialog
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,296 +0,0 @@
|
|||||||
import { defineComponent, h, inject, onMounted, ref, watch, computed } from "vue";
|
|
||||||
import { useDialog } from "./helper";
|
|
||||||
import { Browser } from "../../types";
|
|
||||||
import { isArray, isBoolean } from "../../utils";
|
|
||||||
import { renderNode } from "../../utils/vnode";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-dialog",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
// 是否可见
|
|
||||||
modelValue: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
// 标题
|
|
||||||
title: {
|
|
||||||
type: String,
|
|
||||||
default: "对话框"
|
|
||||||
},
|
|
||||||
// 高度
|
|
||||||
height: String,
|
|
||||||
// 宽度
|
|
||||||
width: {
|
|
||||||
type: String,
|
|
||||||
default: "50%"
|
|
||||||
},
|
|
||||||
// 是否缓存
|
|
||||||
keepAlive: Boolean,
|
|
||||||
// 是否拖动
|
|
||||||
drag: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
// el-dialog 参数
|
|
||||||
props: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 控制按钮
|
|
||||||
controls: {
|
|
||||||
type: Array,
|
|
||||||
default: () => ["fullscreen", "close"]
|
|
||||||
},
|
|
||||||
// 是否隐藏控制按钮
|
|
||||||
hiddenControls: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
// 隐藏头部元素
|
|
||||||
hiddenHeader: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["update:modelValue", "fullscreen-change", "open", "opened", "close", "closed"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const browser = inject("browser") as Browser;
|
|
||||||
|
|
||||||
// 是否全屏
|
|
||||||
const fullscreen = ref<boolean>(props.props.fullscreen);
|
|
||||||
|
|
||||||
// 是否可见
|
|
||||||
const visible = ref<boolean>(props.modelValue);
|
|
||||||
|
|
||||||
// 缓存数
|
|
||||||
const cacheKey = ref<number>(0);
|
|
||||||
|
|
||||||
// 是否全屏
|
|
||||||
const isFullscreen = computed(() => {
|
|
||||||
return browser.isMini ? true : fullscreen.value;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 对话框事件
|
|
||||||
const { setDialog } = useDialog({ isFullscreen, props });
|
|
||||||
|
|
||||||
// 监听绑定值
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
(val: boolean) => {
|
|
||||||
visible.value = val;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// 监听 fullscreen 变化
|
|
||||||
watch(
|
|
||||||
() => props.props.fullscreen,
|
|
||||||
(val: boolean) => {
|
|
||||||
fullscreen.value = val;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
watch(fullscreen, (val: boolean) => {
|
|
||||||
emit("fullscreen-change", val);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(isFullscreen, setDialog);
|
|
||||||
|
|
||||||
function close() {
|
|
||||||
emit("update:modelValue", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭前
|
|
||||||
function beforeClose() {
|
|
||||||
if (props.props["before-close"]) {
|
|
||||||
props.props["before-close"](close);
|
|
||||||
} else {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpen() {
|
|
||||||
// 初始值
|
|
||||||
fullscreen.value = props.props.fullscreen;
|
|
||||||
|
|
||||||
// 是否缓存
|
|
||||||
if (!props.keepAlive) {
|
|
||||||
cacheKey.value += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
setDialog();
|
|
||||||
emit("open");
|
|
||||||
}
|
|
||||||
|
|
||||||
function onOpened() {
|
|
||||||
emit("opened");
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClose() {
|
|
||||||
emit("close");
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClosed() {
|
|
||||||
emit("closed");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换全屏
|
|
||||||
function changeFullscreen(val?: boolean) {
|
|
||||||
fullscreen.value = isBoolean(val) ? Boolean(val) : !fullscreen.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 双击全屏
|
|
||||||
function dblClickFullscreen() {
|
|
||||||
if (isArray(props.controls) && props.controls.includes("fullscreen")) {
|
|
||||||
changeFullscreen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(function () {
|
|
||||||
setDialog();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
visible,
|
|
||||||
fullscreen,
|
|
||||||
isFullscreen,
|
|
||||||
cacheKey,
|
|
||||||
close,
|
|
||||||
onOpen,
|
|
||||||
onOpened,
|
|
||||||
onClose,
|
|
||||||
onClosed,
|
|
||||||
changeFullscreen,
|
|
||||||
beforeClose,
|
|
||||||
dblClickFullscreen
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
const browser = inject("browser") as Browser;
|
|
||||||
|
|
||||||
// 渲染头部
|
|
||||||
function renderHeader() {
|
|
||||||
return ctx.hiddenHeader ? null : (
|
|
||||||
<div class="cl-dialog__header" onDblclick={ctx.dblClickFullscreen}>
|
|
||||||
{/* 标题 */}
|
|
||||||
<span class="cl-dialog__title">{ctx.title}</span>
|
|
||||||
|
|
||||||
{/* 控制按钮 */}
|
|
||||||
<div class="cl-dialog__controls">
|
|
||||||
{ctx.controls.map((vnode: any) => {
|
|
||||||
// 全屏按钮
|
|
||||||
if (vnode === "fullscreen") {
|
|
||||||
// 隐藏全屏
|
|
||||||
if (browser.screen === "xs") {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否显示全屏按钮
|
|
||||||
if (ctx.isFullscreen) {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="minimize"
|
|
||||||
onClick={() => {
|
|
||||||
ctx.changeFullscreen(false);
|
|
||||||
}}>
|
|
||||||
<i class="el-icon-minus" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="maximize"
|
|
||||||
onClick={() => {
|
|
||||||
ctx.changeFullscreen(true);
|
|
||||||
}}>
|
|
||||||
<i class="el-icon-full-screen" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 关闭按钮
|
|
||||||
else if (vnode === "close") {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
class="close"
|
|
||||||
onClick={() => {
|
|
||||||
ctx.beforeClose();
|
|
||||||
}}>
|
|
||||||
<i class="el-icon-close" />
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return renderNode(vnode, {
|
|
||||||
slots: ctx.$slots
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// el-dialog 对话框
|
|
||||||
const ElDialog = (
|
|
||||||
<el-dialog
|
|
||||||
title={ctx.title}
|
|
||||||
width={ctx.width}
|
|
||||||
onOpen={ctx.onOpen}
|
|
||||||
onOpened={ctx.onOpened}
|
|
||||||
onClose={ctx.onClose}
|
|
||||||
onClosed={ctx.onClosed}
|
|
||||||
show-close={false}
|
|
||||||
v-model={ctx.visible}></el-dialog>
|
|
||||||
);
|
|
||||||
|
|
||||||
// 自定义样式
|
|
||||||
const customClass = `cl-dialog cl-dialog--${ctx.$.uid} ${ctx.props.customClass || ""}`;
|
|
||||||
|
|
||||||
// 对话框高度
|
|
||||||
const height = ctx.height ? (ctx.isFullscreen ? `calc(100vh - 46px)` : ctx.height) : null;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{h(
|
|
||||||
ElDialog,
|
|
||||||
{
|
|
||||||
...ctx.props,
|
|
||||||
customClass,
|
|
||||||
fullscreen: ctx.isFullscreen
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title() {
|
|
||||||
return renderHeader();
|
|
||||||
},
|
|
||||||
default() {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
class="cl-dialog__container"
|
|
||||||
style={{ height }}
|
|
||||||
key={ctx.cacheKey}>
|
|
||||||
{ctx.$slots.default && ctx.$slots.default()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
footer() {
|
|
||||||
return (
|
|
||||||
<div class="cl-dialog__footer">
|
|
||||||
{ctx.$slots.footer && ctx.$slots.footer()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,15 +0,0 @@
|
|||||||
import { defineComponent } from "vue";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-error-message",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
title: String
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
return () => {
|
|
||||||
return <el-alert title={props.title} type="error"></el-alert>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,21 +0,0 @@
|
|||||||
import { defineComponent } from "vue";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-filter",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
label: String
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
return (
|
|
||||||
<div class="cl-filter">
|
|
||||||
<span class="cl-filter__label" v-show={ctx.label}>
|
|
||||||
{ctx.label}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
{ctx.$slots.default ? ctx.$slots.default() : null}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,11 +0,0 @@
|
|||||||
import { defineComponent } from "vue";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-flex1",
|
|
||||||
|
|
||||||
setup(_, { slots }) {
|
|
||||||
return () => {
|
|
||||||
return <div class="cl-flex1">{slots.default ? slots.default() : null}</div>;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,119 +0,0 @@
|
|||||||
import { defineComponent, nextTick, onMounted, reactive, ref, watch } from "vue";
|
|
||||||
import { useRefs } from "../hooks/core";
|
|
||||||
import { isArray, isEmpty } from "../utils";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-form-tabs",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
modelValue: [String, Number],
|
|
||||||
labels: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
justify: {
|
|
||||||
type: String,
|
|
||||||
default: "center"
|
|
||||||
},
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: "#409EFF"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["update:modelValue", "change"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const { refs, setRefs }: any = useRefs();
|
|
||||||
|
|
||||||
// 标识
|
|
||||||
const active = ref<any>("");
|
|
||||||
|
|
||||||
// 切换列表
|
|
||||||
const list = ref<any[]>([]);
|
|
||||||
|
|
||||||
// 下划线
|
|
||||||
const line = reactive<any>({
|
|
||||||
width: "",
|
|
||||||
offsetLeft: ""
|
|
||||||
});
|
|
||||||
|
|
||||||
function update(val: string | number) {
|
|
||||||
nextTick(() => {
|
|
||||||
const index = list.value.findIndex((e) => e.value === val);
|
|
||||||
const item = refs.value[`tab-${index}`];
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
// 下划线位置
|
|
||||||
line.width = item.clientWidth + "px";
|
|
||||||
line.transform = `translateX(${item.offsetLeft}px)`;
|
|
||||||
line.backgroundColor = props.color;
|
|
||||||
|
|
||||||
// 靠左位置
|
|
||||||
let left: number = item.offsetLeft + item.clientWidth / 2 - 414 / 2 + 15;
|
|
||||||
|
|
||||||
if (left < 0) {
|
|
||||||
left = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置滚动距离
|
|
||||||
refs.value.tabs.scrollLeft = left;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
active.value = val;
|
|
||||||
emit("update:modelValue", val);
|
|
||||||
emit("change", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听绑定值变化
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
(val: any) => {
|
|
||||||
update(val);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
onMounted(function () {
|
|
||||||
if (isArray(props.labels) && props.labels.length > 0) {
|
|
||||||
list.value = props.labels;
|
|
||||||
update(isEmpty(props.modelValue) ? list.value[0].value : props.modelValue);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
active,
|
|
||||||
list,
|
|
||||||
line,
|
|
||||||
refs,
|
|
||||||
setRefs,
|
|
||||||
update
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
return (
|
|
||||||
<div class="cl-form-tabs">
|
|
||||||
<ul style={{ textAlign: ctx.justify }} ref={ctx.setRefs("tabs")}>
|
|
||||||
{ctx.list.map((e: any, i: number) => {
|
|
||||||
return (
|
|
||||||
<li
|
|
||||||
ref={ctx.setRefs(`tab-${i}`)}
|
|
||||||
class={{ "is-active": e.value === ctx.active }}
|
|
||||||
style={{
|
|
||||||
color: e.value === ctx.active ? ctx.color : "#444"
|
|
||||||
}}
|
|
||||||
onClick={() => {
|
|
||||||
ctx.update(e.value);
|
|
||||||
}}>
|
|
||||||
{e.label}
|
|
||||||
</li>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
|
|
||||||
{ctx.line.width && <div class="cl-form-tabs__line" style={ctx.line}></div>}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,131 +0,0 @@
|
|||||||
import { dataset } from "../../utils";
|
|
||||||
import { ref } from "vue";
|
|
||||||
|
|
||||||
export function useAction({ conf, form, refs }: any) {
|
|
||||||
// 加载状态
|
|
||||||
const loading = ref<boolean>(false);
|
|
||||||
|
|
||||||
// 设置数据
|
|
||||||
function set({ prop, options, hidden, path }: any, data?: any): any {
|
|
||||||
let p: string = path || "";
|
|
||||||
|
|
||||||
if (prop) {
|
|
||||||
p = `items[prop:${prop}]`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (options) {
|
|
||||||
p += `.component.options`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hidden) {
|
|
||||||
p += ".hidden";
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataset(conf, p, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取表单值
|
|
||||||
function getForm(prop: string) {
|
|
||||||
return prop ? form[prop] : form;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置表单值
|
|
||||||
function setForm(prop: string, value: any) {
|
|
||||||
form[prop] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置路径数据
|
|
||||||
function setData(path: string, value: any) {
|
|
||||||
set({ path }, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置表单项的下拉数据列表
|
|
||||||
function setOptions(prop: string, value: Array<any>) {
|
|
||||||
set({ options: true, prop }, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 切换表单项的显示、隐藏
|
|
||||||
function toggleItem(prop: string, value?: boolean) {
|
|
||||||
if (value === undefined) {
|
|
||||||
value = set({ prop, hidden: true });
|
|
||||||
}
|
|
||||||
|
|
||||||
set({ hidden: true, prop }, !value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对部分表单项隐藏
|
|
||||||
function hiddenItem(...props: Array<string>) {
|
|
||||||
props.forEach((prop: string) => {
|
|
||||||
set({ hidden: true, prop }, true);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对部分表单项显示
|
|
||||||
function showItem(...props: Array<string>) {
|
|
||||||
props.forEach((prop: string) => {
|
|
||||||
set({ hidden: true, prop }, false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示表单加载状态
|
|
||||||
function showLoading() {
|
|
||||||
loading.value = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 隐藏表单加载状态
|
|
||||||
function hiddenLoading() {
|
|
||||||
loading.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对整个表单进行重置
|
|
||||||
function resetFields() {
|
|
||||||
if (refs.value.form) {
|
|
||||||
refs.value.form.resetFields();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 移除表单项的校验结果
|
|
||||||
function clearValidate(props?: string | Array<any>) {
|
|
||||||
if (refs.value.form) {
|
|
||||||
return refs.value.form.clearValidate(props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对部分表单字段进行校验
|
|
||||||
function validateField(props?: string | Array<any>, callback?: Function) {
|
|
||||||
if (refs.value.form) {
|
|
||||||
refs.value.form.validateField(props, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 对整个表单进行校验
|
|
||||||
function validate(callback?: Function) {
|
|
||||||
if (refs.value.form) {
|
|
||||||
refs.value.form.validate(callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否展开表单项
|
|
||||||
function collapseItem(e: any) {
|
|
||||||
clearValidate(e.prop);
|
|
||||||
e.collapse = !e.collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
loading,
|
|
||||||
showLoading,
|
|
||||||
hiddenLoading,
|
|
||||||
collapseItem,
|
|
||||||
getForm,
|
|
||||||
setForm,
|
|
||||||
setData,
|
|
||||||
setOptions,
|
|
||||||
toggleItem,
|
|
||||||
hiddenItem,
|
|
||||||
showItem,
|
|
||||||
resetFields,
|
|
||||||
clearValidate,
|
|
||||||
validateField,
|
|
||||||
validate
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,517 +0,0 @@
|
|||||||
import { defineComponent, h, inject, nextTick, provide, reactive, ref, watch } from "vue";
|
|
||||||
import cloneDeep from "clone-deep";
|
|
||||||
import { useAction } from "./helper";
|
|
||||||
import { useRefs, useForm } from "../../hooks/core";
|
|
||||||
import { deepMerge, isBoolean, isEmpty, isObject, isString } from "../../utils";
|
|
||||||
import Parse from "../../utils/parse";
|
|
||||||
import { renderNode } from "../../utils/vnode";
|
|
||||||
import { Browser, Form } from "../../types";
|
|
||||||
import FormHook from "../../hooks/form";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-form",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
modelValue: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["update:modelValue"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const { refs, setRefs }: any = useRefs();
|
|
||||||
|
|
||||||
// 设置表单值
|
|
||||||
const { setFormData } = useForm(props);
|
|
||||||
|
|
||||||
// 表单是否可见
|
|
||||||
const visible = ref<boolean>(false);
|
|
||||||
|
|
||||||
// 表单提交保存状态
|
|
||||||
const saving = ref<boolean>(false);
|
|
||||||
|
|
||||||
// 选项卡
|
|
||||||
const tabActive = ref<any>(null);
|
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const form = setFormData();
|
|
||||||
|
|
||||||
// 表单配置
|
|
||||||
const conf = reactive<Form>({
|
|
||||||
title: "自定义表单",
|
|
||||||
width: "50%",
|
|
||||||
props: {
|
|
||||||
size: "small",
|
|
||||||
labelWidth: "100px"
|
|
||||||
},
|
|
||||||
on: {},
|
|
||||||
op: {
|
|
||||||
hidden: false,
|
|
||||||
saveButtonText: "保存",
|
|
||||||
closeButtonText: "取消",
|
|
||||||
buttons: ["close", "save"]
|
|
||||||
},
|
|
||||||
dialog: {
|
|
||||||
props: {
|
|
||||||
fullscreen: false,
|
|
||||||
"close-on-click-modal": false,
|
|
||||||
"append-to-body": true
|
|
||||||
},
|
|
||||||
hiddenControls: false,
|
|
||||||
controls: ["fullscreen", "close"]
|
|
||||||
},
|
|
||||||
items: [],
|
|
||||||
_data: {}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 表单动作
|
|
||||||
const {
|
|
||||||
loading,
|
|
||||||
showLoading,
|
|
||||||
hiddenLoading,
|
|
||||||
collapseItem,
|
|
||||||
getForm,
|
|
||||||
setForm,
|
|
||||||
setData,
|
|
||||||
setOptions,
|
|
||||||
toggleItem,
|
|
||||||
hiddenItem,
|
|
||||||
showItem,
|
|
||||||
resetFields,
|
|
||||||
clearValidate,
|
|
||||||
validateField,
|
|
||||||
validate
|
|
||||||
} = useAction({ conf, form, refs });
|
|
||||||
|
|
||||||
// 更新绑定值
|
|
||||||
watch(form, (val: any) => {
|
|
||||||
emit("update:modelValue", val);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 提供
|
|
||||||
provide("form", form);
|
|
||||||
|
|
||||||
// 请求表单保存状态
|
|
||||||
function done() {
|
|
||||||
saving.value = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 关闭表单
|
|
||||||
function close() {
|
|
||||||
visible.value = false;
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
function onClosed() {
|
|
||||||
tabActive.value = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表单关闭前事件
|
|
||||||
function beforeClose() {
|
|
||||||
if (conf.on?.close) {
|
|
||||||
conf.on.close(close);
|
|
||||||
} else {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 清空表单验证
|
|
||||||
function clear() {
|
|
||||||
for (const i in form) {
|
|
||||||
delete form[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
clearValidate();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表单提交
|
|
||||||
function submit(callback?: Function) {
|
|
||||||
// 验证表单
|
|
||||||
refs.value.form.validate(async (valid: boolean, error: any) => {
|
|
||||||
if (valid) {
|
|
||||||
saving.value = true;
|
|
||||||
|
|
||||||
// 拷贝表单值
|
|
||||||
const d = cloneDeep(form);
|
|
||||||
|
|
||||||
// 过滤隐藏的表单项
|
|
||||||
conf.items.forEach((e: any) => {
|
|
||||||
if (e._hidden) {
|
|
||||||
delete d[e.prop];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (e.hook) {
|
|
||||||
d[e.prop] = FormHook.submit(d[e.prop], e.hook, d);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const submit = callback || conf.on?.submit;
|
|
||||||
|
|
||||||
// 提交事件
|
|
||||||
if (submit) {
|
|
||||||
submit(d, {
|
|
||||||
done,
|
|
||||||
close
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error("Not found callback function");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 判断是否使用form-tabs,切换到对应的选项卡
|
|
||||||
const keys = Object.keys(error);
|
|
||||||
|
|
||||||
if (tabActive.value) {
|
|
||||||
const item = conf.items.find((e) => e.prop === keys[0]);
|
|
||||||
|
|
||||||
if (item) {
|
|
||||||
tabActive.value = item.group;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开表单
|
|
||||||
function open(options?: Form) {
|
|
||||||
if (!options) {
|
|
||||||
options = {
|
|
||||||
items: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
clear();
|
|
||||||
|
|
||||||
// 合并配置
|
|
||||||
for (const i in conf) {
|
|
||||||
switch (i) {
|
|
||||||
case "items":
|
|
||||||
conf.items = cloneDeep(options.items || []);
|
|
||||||
break;
|
|
||||||
case "title":
|
|
||||||
case "width":
|
|
||||||
conf[i] = options[i];
|
|
||||||
break;
|
|
||||||
case "props":
|
|
||||||
case "on":
|
|
||||||
case "op":
|
|
||||||
case "dialog":
|
|
||||||
case "_data":
|
|
||||||
conf[i] = deepMerge(conf[i], options[i] || {});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示对话框
|
|
||||||
visible.value = true;
|
|
||||||
|
|
||||||
// 预设表单值
|
|
||||||
if (options?.form) {
|
|
||||||
for (const i in options.form) {
|
|
||||||
form[i] = options.form[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置表单数据
|
|
||||||
conf.items.map((e: any) => {
|
|
||||||
if (e.prop) {
|
|
||||||
form[e.prop] = FormHook.bind(
|
|
||||||
isEmpty(form[e.prop]) ? cloneDeep(e.value) : form[e.prop],
|
|
||||||
e.hook,
|
|
||||||
form
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 打开回调
|
|
||||||
nextTick(() => {
|
|
||||||
if (conf.on?.open) {
|
|
||||||
conf.on.open(form, {
|
|
||||||
close,
|
|
||||||
submit,
|
|
||||||
done
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
showLoading,
|
|
||||||
hiddenLoading,
|
|
||||||
collapseItem,
|
|
||||||
getForm,
|
|
||||||
setForm,
|
|
||||||
setData,
|
|
||||||
setOptions,
|
|
||||||
toggleItem,
|
|
||||||
hiddenItem,
|
|
||||||
showItem,
|
|
||||||
resetFields,
|
|
||||||
clearValidate,
|
|
||||||
validateField,
|
|
||||||
validate
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 重新绑定表单数据
|
|
||||||
function reBindForm(data: any) {
|
|
||||||
for (const i in data) {
|
|
||||||
const d: any = conf.items.find((e) => e.prop === i);
|
|
||||||
form[i] = d ? FormHook.bind(data[i], d.hook, form) : data[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
visible,
|
|
||||||
saving,
|
|
||||||
tabActive,
|
|
||||||
form,
|
|
||||||
refs,
|
|
||||||
setRefs,
|
|
||||||
conf,
|
|
||||||
loading,
|
|
||||||
open,
|
|
||||||
beforeClose,
|
|
||||||
close,
|
|
||||||
onClosed,
|
|
||||||
done,
|
|
||||||
clear,
|
|
||||||
submit,
|
|
||||||
reBindForm,
|
|
||||||
showLoading,
|
|
||||||
hiddenLoading,
|
|
||||||
collapseItem,
|
|
||||||
getForm,
|
|
||||||
setForm,
|
|
||||||
setData,
|
|
||||||
setOptions,
|
|
||||||
toggleItem,
|
|
||||||
hiddenItem,
|
|
||||||
showItem,
|
|
||||||
resetFields,
|
|
||||||
clearValidate,
|
|
||||||
validateField,
|
|
||||||
validate
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
const browser = inject("browser") as Browser;
|
|
||||||
const { props, op, title, width, dialog, _data } = ctx.conf;
|
|
||||||
|
|
||||||
// 渲染表单及表单项
|
|
||||||
const renderForm = () => {
|
|
||||||
// 表单项列表
|
|
||||||
const children = ctx.conf.items.map((e: any) => {
|
|
||||||
if (e.type == "tabs") {
|
|
||||||
return <cl-form-tabs v-model={ctx.tabActive} {...e.props}></cl-form-tabs>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 隐藏处理
|
|
||||||
e._hidden = Parse("hidden", {
|
|
||||||
value: e.hidden,
|
|
||||||
scope: ctx.form,
|
|
||||||
data: _data
|
|
||||||
});
|
|
||||||
|
|
||||||
// 分组
|
|
||||||
e._group =
|
|
||||||
isEmpty(ctx.tabActive) || isEmpty(e.group) ? true : e.group === ctx.tabActive;
|
|
||||||
|
|
||||||
// Flex handler
|
|
||||||
if (isEmpty(e.flex)) {
|
|
||||||
e._flex = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
!e._hidden && (
|
|
||||||
<el-col span={24} {...e}>
|
|
||||||
{e.component &&
|
|
||||||
h(
|
|
||||||
<el-form-item v-show={e._group}></el-form-item>,
|
|
||||||
{
|
|
||||||
prop: e.prop,
|
|
||||||
rules: e.rules,
|
|
||||||
...e.props
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: () => {
|
|
||||||
let d: any = {
|
|
||||||
text: "",
|
|
||||||
tip: "",
|
|
||||||
icon: ""
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isString(e.label)) {
|
|
||||||
d.text = e.label;
|
|
||||||
} else if (isObject(e.label)) {
|
|
||||||
d = e.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<el-tooltip
|
|
||||||
effect="dark"
|
|
||||||
placement="top"
|
|
||||||
content={d.tip}
|
|
||||||
disabled={!d.tip}>
|
|
||||||
<span>
|
|
||||||
{d.text}
|
|
||||||
{d.icon && <i class={d.icon}></i>}
|
|
||||||
</span>
|
|
||||||
</el-tooltip>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
default: () => {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
{/* Form item */}
|
|
||||||
<div class="cl-form-item">
|
|
||||||
{["prepend", "component", "append"].map(
|
|
||||||
(name) => {
|
|
||||||
return (
|
|
||||||
e[name] && (
|
|
||||||
<div
|
|
||||||
v-show={!e.collapse}
|
|
||||||
class={[
|
|
||||||
`cl-form-item__${name}`,
|
|
||||||
{
|
|
||||||
"is-flex":
|
|
||||||
e._flex
|
|
||||||
}
|
|
||||||
]}>
|
|
||||||
{renderNode(e[name], {
|
|
||||||
prop: e.prop,
|
|
||||||
scope: ctx.form,
|
|
||||||
slots: ctx.$slots
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{/* Collapse button */}
|
|
||||||
{isBoolean(e.collapse) && (
|
|
||||||
<div
|
|
||||||
class="cl-form-item__collapse"
|
|
||||||
onClick={() => {
|
|
||||||
ctx.collapseItem(e);
|
|
||||||
}}>
|
|
||||||
<el-divider content-position="center">
|
|
||||||
{e.collapse ? (
|
|
||||||
<span>
|
|
||||||
查看更多
|
|
||||||
<i class="el-icon-arrow-down"></i>
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
<span>
|
|
||||||
隐藏内容
|
|
||||||
<i class="el-icon-arrow-up"></i>
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</el-divider>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</el-col>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// el-form
|
|
||||||
const ElForm = (
|
|
||||||
<el-form
|
|
||||||
ref={ctx.setRefs("form")}
|
|
||||||
label-position={browser.isMini ? "top" : ""}
|
|
||||||
size="small"
|
|
||||||
label-width="100px"
|
|
||||||
disabled={ctx.saving}
|
|
||||||
model={ctx.form}></el-form>
|
|
||||||
);
|
|
||||||
|
|
||||||
return h(ElForm, props, {
|
|
||||||
default: () => {
|
|
||||||
return (
|
|
||||||
<el-row gutter={10} v-loading={ctx.loading}>
|
|
||||||
{children}
|
|
||||||
</el-row>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 渲染表单按钮
|
|
||||||
function renderFooter() {
|
|
||||||
const { hidden, buttons, saveButtonText, closeButtonText } = op;
|
|
||||||
const { size = "small" } = props;
|
|
||||||
|
|
||||||
return hidden
|
|
||||||
? null
|
|
||||||
: buttons.map((vnode: any) => {
|
|
||||||
if (vnode == "save") {
|
|
||||||
return (
|
|
||||||
<el-button
|
|
||||||
{...{
|
|
||||||
size,
|
|
||||||
type: "success",
|
|
||||||
disabled: ctx.loading,
|
|
||||||
loading: ctx.saving,
|
|
||||||
onClick: () => {
|
|
||||||
ctx.submit();
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
{saveButtonText}
|
|
||||||
</el-button>
|
|
||||||
);
|
|
||||||
} else if (vnode == "close") {
|
|
||||||
return (
|
|
||||||
<el-button
|
|
||||||
{...{
|
|
||||||
size,
|
|
||||||
onClick: () => {
|
|
||||||
ctx.beforeClose();
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
{closeButtonText}
|
|
||||||
</el-button>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return renderNode(vnode, {
|
|
||||||
scope: ctx.form,
|
|
||||||
slots: ctx.$slots
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return h(
|
|
||||||
<cl-dialog v-model={ctx.visible}></cl-dialog>,
|
|
||||||
{
|
|
||||||
title,
|
|
||||||
width,
|
|
||||||
...dialog,
|
|
||||||
props: {
|
|
||||||
...dialog.props,
|
|
||||||
"before-close": ctx.beforeClose
|
|
||||||
},
|
|
||||||
onClosed: ctx.onClosed
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default() {
|
|
||||||
return (
|
|
||||||
<div class="cl-form">
|
|
||||||
<div class="cl-form__container">{renderForm()}</div>
|
|
||||||
<div class="cl-form__footer">{renderFooter()}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,39 +0,0 @@
|
|||||||
import Crud from "./crud/index";
|
|
||||||
import AddBtn from "./add-btn";
|
|
||||||
import AdvBtn from "./adv-btn";
|
|
||||||
import AdvSearch from "./adv-search";
|
|
||||||
import Flex from "./flex1";
|
|
||||||
import Form from "./form";
|
|
||||||
import FormTabs from "./form-tabs";
|
|
||||||
import MultiDeleteBtn from "./multi-delete-btn";
|
|
||||||
import Pagination from "./pagination";
|
|
||||||
import Query from "./query";
|
|
||||||
import RefreshBtn from "./refresh-btn";
|
|
||||||
import SearchKey from "./search-key";
|
|
||||||
import Table from "./table/index";
|
|
||||||
import Upsert from "./upsert/index";
|
|
||||||
import Dialog from "./dialog";
|
|
||||||
import Filter from "./filter";
|
|
||||||
import ErrorMessage from "./error-message";
|
|
||||||
import ContextMenu from "./context-menu/context-menu";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Crud,
|
|
||||||
AddBtn,
|
|
||||||
AdvBtn,
|
|
||||||
AdvSearch,
|
|
||||||
Flex,
|
|
||||||
Form,
|
|
||||||
FormTabs,
|
|
||||||
MultiDeleteBtn,
|
|
||||||
Pagination,
|
|
||||||
Query,
|
|
||||||
RefreshBtn,
|
|
||||||
SearchKey,
|
|
||||||
Table,
|
|
||||||
Upsert,
|
|
||||||
Dialog,
|
|
||||||
Filter,
|
|
||||||
ErrorMessage,
|
|
||||||
ContextMenu
|
|
||||||
};
|
|
@ -1,26 +0,0 @@
|
|||||||
import { defineComponent, inject } from "vue";
|
|
||||||
import { Crud } from "../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-multi-delete-btn",
|
|
||||||
|
|
||||||
setup(_, { slots }) {
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
return (
|
|
||||||
crud.getPermission("delete") && (
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="danger"
|
|
||||||
disabled={crud.selection.length === 0}
|
|
||||||
onClick={() => {
|
|
||||||
crud.rowDelete(...crud.selection);
|
|
||||||
}}>
|
|
||||||
{slots.default ? slots.default() : crud.dict.label.multiDelete}
|
|
||||||
</el-button>
|
|
||||||
)
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,84 +0,0 @@
|
|||||||
import { defineComponent, h, inject, ref, watch } from "vue";
|
|
||||||
import { Crud, Mitt } from "../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-pagination",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
props: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
setup(props) {
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
const mitt = inject("mitt") as Mitt;
|
|
||||||
|
|
||||||
// 总数
|
|
||||||
const total = ref<number>(0);
|
|
||||||
// 当前页码
|
|
||||||
const currentPage = ref<number>(1);
|
|
||||||
// 每页大小
|
|
||||||
const pageSize = ref<number>(20);
|
|
||||||
|
|
||||||
const onCurrentChange = (index: number) => {
|
|
||||||
crud.refresh({
|
|
||||||
page: index
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSizeChange = (size: number) => {
|
|
||||||
crud.refresh({
|
|
||||||
page: 1,
|
|
||||||
size
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const setPagination = (res: any) => {
|
|
||||||
if (res) {
|
|
||||||
currentPage.value = res.currentPage || res.page || 1;
|
|
||||||
pageSize.value = res.pageSize || res.size || 20;
|
|
||||||
total.value = res.total | 0;
|
|
||||||
crud.params.size = pageSize.value;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
mitt.on("crud.refresh", ({ pagination }: any) => {
|
|
||||||
setPagination(pagination);
|
|
||||||
});
|
|
||||||
|
|
||||||
watch(() => props.props, setPagination, {
|
|
||||||
immediate: true
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
total,
|
|
||||||
currentPage,
|
|
||||||
pageSize,
|
|
||||||
onCurrentChange,
|
|
||||||
onSizeChange,
|
|
||||||
setPagination
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
const ElPagination = (
|
|
||||||
<el-pagination
|
|
||||||
background
|
|
||||||
page-sizes={[10, 20, 30, 40, 50, 100]}
|
|
||||||
layout={"total, sizes, prev, pager, next, jumper"}
|
|
||||||
{...ctx.props}></el-pagination>
|
|
||||||
);
|
|
||||||
|
|
||||||
return h(ElPagination, {
|
|
||||||
onSizeChange: ctx.onSizeChange,
|
|
||||||
onCurrentChange: ctx.onCurrentChange,
|
|
||||||
total: ctx.total,
|
|
||||||
"current-page": ctx.currentPage,
|
|
||||||
"page-size": ctx.pageSize
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,118 +0,0 @@
|
|||||||
import { defineComponent, inject, ref, watch } from "vue";
|
|
||||||
import { Crud } from "../types";
|
|
||||||
import { isArray } from "../utils";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-query",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
modelValue: null,
|
|
||||||
list: {
|
|
||||||
type: Array,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
field: {
|
|
||||||
type: String,
|
|
||||||
default: "query"
|
|
||||||
},
|
|
||||||
multiple: Boolean,
|
|
||||||
callback: Function
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["update:modelValue", "change"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
|
|
||||||
const list2 = ref<Array<any>>([]);
|
|
||||||
|
|
||||||
// 更新数据列表
|
|
||||||
const update = () => {
|
|
||||||
let arr: Array<any> = [];
|
|
||||||
|
|
||||||
if (isArray(props.modelValue)) {
|
|
||||||
arr = props.modelValue;
|
|
||||||
} else {
|
|
||||||
arr = [props.modelValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!props.multiple) {
|
|
||||||
arr.splice(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 默认选择
|
|
||||||
list2.value = (props.list || []).map((e: any) => {
|
|
||||||
e.active = arr.some((v) => v === e.value);
|
|
||||||
return e;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
update();
|
|
||||||
|
|
||||||
// 点击选择项
|
|
||||||
const selectItem = (event: any, item: any) => {
|
|
||||||
if (item.active) {
|
|
||||||
item.active = false;
|
|
||||||
} else {
|
|
||||||
if (props.multiple) {
|
|
||||||
item.active = true;
|
|
||||||
} else {
|
|
||||||
list2.value.map((e: any) => {
|
|
||||||
e.active = e.value == item.value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 过滤未选中的
|
|
||||||
const selection = list2.value.filter((e: any) => e.active).map((e: any) => e.value);
|
|
||||||
// 处理多选情况
|
|
||||||
const value = props.multiple ? selection : selection[0];
|
|
||||||
|
|
||||||
// 请求回调
|
|
||||||
if (props.callback) {
|
|
||||||
props.callback(value);
|
|
||||||
} else {
|
|
||||||
crud.refresh({
|
|
||||||
[props.field]: value
|
|
||||||
});
|
|
||||||
|
|
||||||
emit("change", value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 阻止默认事件
|
|
||||||
event.preventDefault();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 监听绑定值,更新数据列表
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
() => {
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
list2,
|
|
||||||
selectItem
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
return (
|
|
||||||
<div class="cl-query">
|
|
||||||
{ctx.list2.map((item: any, index: number) => {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
class={{ "is-active": item.active }}
|
|
||||||
key={index}
|
|
||||||
onClick={(event) => {
|
|
||||||
ctx.selectItem(event, item);
|
|
||||||
}}>
|
|
||||||
<span>{item.label}</span>
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,23 +0,0 @@
|
|||||||
import { defineComponent, inject } from "vue";
|
|
||||||
import { Crud } from "../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-refresh-btn",
|
|
||||||
|
|
||||||
setup(props, { slots }) {
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
return (
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
onClick={() => {
|
|
||||||
crud.refresh();
|
|
||||||
}}
|
|
||||||
{...props}>
|
|
||||||
{slots.default ? slots.default() : crud.dict.label.refresh}
|
|
||||||
</el-button>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,129 +0,0 @@
|
|||||||
import { defineComponent, inject, ref } from "vue";
|
|
||||||
import { Crud } from "../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-search-key",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
// 绑定值
|
|
||||||
modelValue: String,
|
|
||||||
// 选中字段
|
|
||||||
field: {
|
|
||||||
type: String,
|
|
||||||
default: "keyWord"
|
|
||||||
},
|
|
||||||
// 字段列表
|
|
||||||
fieldList: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
// 搜索时的钩子
|
|
||||||
onSearch: Function,
|
|
||||||
// 输入框占位内容
|
|
||||||
placeholder: {
|
|
||||||
type: String,
|
|
||||||
default: "请输入关键字"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["update:modelValue", "change", "field-change"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
|
|
||||||
// 选中字段
|
|
||||||
const selectField = ref<string>(props.field);
|
|
||||||
|
|
||||||
// 搜索内容
|
|
||||||
const value = ref<string>(props.modelValue || "");
|
|
||||||
|
|
||||||
// 搜索
|
|
||||||
function search() {
|
|
||||||
const params: any = {};
|
|
||||||
|
|
||||||
props.fieldList.forEach((e: any) => {
|
|
||||||
params[e.value] = null;
|
|
||||||
});
|
|
||||||
|
|
||||||
function next(newParams?: any) {
|
|
||||||
crud.refresh({
|
|
||||||
page: 1,
|
|
||||||
...params,
|
|
||||||
[selectField.value]: value.value,
|
|
||||||
...newParams
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (props.onSearch) {
|
|
||||||
props.onSearch(params, { next });
|
|
||||||
} else {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回车搜索
|
|
||||||
function onKeydown({ keyCode }: any) {
|
|
||||||
if (keyCode === 13) {
|
|
||||||
search();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听输入
|
|
||||||
function onInput(val: string) {
|
|
||||||
emit("update:modelValue", val);
|
|
||||||
emit("change", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听字段选择
|
|
||||||
function onFieldChange() {
|
|
||||||
emit("field-change", selectField.value);
|
|
||||||
onInput("");
|
|
||||||
value.value = "";
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
value,
|
|
||||||
selectField,
|
|
||||||
search,
|
|
||||||
onKeydown,
|
|
||||||
onInput,
|
|
||||||
onFieldChange
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
return (
|
|
||||||
<div class="cl-search-key">
|
|
||||||
<el-select
|
|
||||||
class="cl-search-key__select"
|
|
||||||
filterable
|
|
||||||
size="mini"
|
|
||||||
v-model={ctx.selectField}
|
|
||||||
v-show={ctx.fieldList.length > 0}
|
|
||||||
onChange={ctx.onFieldChange}>
|
|
||||||
{ctx.fieldList.map((e: any, i: number) => (
|
|
||||||
<el-option key={i} label={e.label} value={e.value} />
|
|
||||||
))}
|
|
||||||
</el-select>
|
|
||||||
|
|
||||||
<el-input
|
|
||||||
class="cl-search-key__input"
|
|
||||||
v-model={ctx.value}
|
|
||||||
placeholder={ctx.placeholder}
|
|
||||||
onKeydown={ctx.onKeydown}
|
|
||||||
onInput={ctx.onInput}
|
|
||||||
clearable
|
|
||||||
size="mini"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<el-button
|
|
||||||
class="cl-search-key__button"
|
|
||||||
type="primary"
|
|
||||||
size="mini"
|
|
||||||
onClick={ctx.search}>
|
|
||||||
搜索
|
|
||||||
</el-button>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,67 +0,0 @@
|
|||||||
export function useElTableApi({ refs }: any) {
|
|
||||||
const clearSelection = () => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.clearSelection();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleRowSelection = (row: any, selected?: boolean) => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.toggleRowSelection(row, selected);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleAllSelection = () => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.toggleAllSelection();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleRowExpansion = (row: any, expanded?: boolean) => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.toggleRowExpansion(row, expanded);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const setCurrentRow = (row: any) => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.setCurrentRow(row);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearSort = () => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.clearSort();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearFilter = (columnKey: any) => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.clearFilter(columnKey);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const doLayout = () => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.doLayout();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const sort = (prop: string, order: string) => {
|
|
||||||
if (refs.value.table) {
|
|
||||||
refs.value.table.sort(prop, order);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
clearSelection,
|
|
||||||
toggleRowSelection,
|
|
||||||
toggleAllSelection,
|
|
||||||
toggleRowExpansion,
|
|
||||||
setCurrentRow,
|
|
||||||
clearSort,
|
|
||||||
clearFilter,
|
|
||||||
doLayout,
|
|
||||||
sort
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,543 +0,0 @@
|
|||||||
import { defineComponent, h, inject, nextTick, onMounted, ref } from "vue";
|
|
||||||
import type { PropType } from "vue";
|
|
||||||
import ContextMenu from "../context-menu/index";
|
|
||||||
import { useElTableApi } from "./helper";
|
|
||||||
import { cloneDeep, isArray, isEmpty, isFunction, isNull, isBoolean } from "../../utils";
|
|
||||||
import { renderNode } from "../../utils/vnode";
|
|
||||||
import { useRefs } from "../../hooks/core";
|
|
||||||
import { Crud, Mitt, Browser, TableColumn } from "../../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-table",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
columns: {
|
|
||||||
type: Array as PropType<TableColumn[]>,
|
|
||||||
required: true,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
on: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
height: Number,
|
|
||||||
// 是否自动计算表格高度
|
|
||||||
autoHeight: {
|
|
||||||
type: Boolean,
|
|
||||||
default: true
|
|
||||||
},
|
|
||||||
// 开启右键菜单
|
|
||||||
contextMenu: {
|
|
||||||
type: [Boolean, Array],
|
|
||||||
default: undefined
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["selection-change"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const { refs, setRefs }: any = useRefs();
|
|
||||||
|
|
||||||
// 参数注入
|
|
||||||
const mitt = inject("mitt") as Mitt;
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
|
|
||||||
// el-table api
|
|
||||||
const {
|
|
||||||
clearSelection,
|
|
||||||
toggleRowSelection,
|
|
||||||
toggleAllSelection,
|
|
||||||
toggleRowExpansion,
|
|
||||||
setCurrentRow,
|
|
||||||
clearSort,
|
|
||||||
clearFilter,
|
|
||||||
doLayout,
|
|
||||||
sort
|
|
||||||
} = useElTableApi({ refs });
|
|
||||||
|
|
||||||
// 是否可见,用于解决一些显示隐藏的副作用
|
|
||||||
const visible = ref<boolean>(true);
|
|
||||||
|
|
||||||
// 列表数据
|
|
||||||
const data = ref<Array<any>>([]);
|
|
||||||
|
|
||||||
// 最大高度
|
|
||||||
const maxHeight = ref<number>(0);
|
|
||||||
|
|
||||||
// 改变排序
|
|
||||||
function changeSort(prop: string, order: string) {
|
|
||||||
if (order === "desc") {
|
|
||||||
order = "descending";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (order === "asc") {
|
|
||||||
order = "ascending";
|
|
||||||
}
|
|
||||||
|
|
||||||
sort(prop, order);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 多选框选择
|
|
||||||
function onSelectionChange(selection: Array<any>) {
|
|
||||||
crud.selection.splice(0, crud.selection.length, ...selection);
|
|
||||||
emit("selection-change", selection);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 排序监听
|
|
||||||
function onSortChange({ prop, order }: any) {
|
|
||||||
if (order === "descending") {
|
|
||||||
order = "desc";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (order === "ascending") {
|
|
||||||
order = "asc";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!order) {
|
|
||||||
prop = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
crud.refresh({
|
|
||||||
prop,
|
|
||||||
order,
|
|
||||||
page: 1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 右键菜单
|
|
||||||
function onRowContextMenu(row: any, column: any, event: any) {
|
|
||||||
// 菜单配置
|
|
||||||
const cm: any =
|
|
||||||
isEmpty(props.contextMenu) && !isArray(props.contextMenu)
|
|
||||||
? crud.table!["context-menu"]
|
|
||||||
: props.contextMenu;
|
|
||||||
|
|
||||||
// 菜单按钮
|
|
||||||
let buttons = ["refresh", "check", "edit", "delete", "order-asc", "order-desc"];
|
|
||||||
// 是否开启
|
|
||||||
let enable = false;
|
|
||||||
|
|
||||||
if (cm) {
|
|
||||||
if (isArray(cm)) {
|
|
||||||
buttons = cm || [];
|
|
||||||
enable = Boolean(buttons.length > 0);
|
|
||||||
} else {
|
|
||||||
enable = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (enable) {
|
|
||||||
// 解析按钮
|
|
||||||
const list = buttons
|
|
||||||
.map((e: any) => {
|
|
||||||
switch (e) {
|
|
||||||
case "refresh":
|
|
||||||
return {
|
|
||||||
label: "刷新",
|
|
||||||
callback(_: any, done: Function) {
|
|
||||||
crud.refresh();
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
case "edit":
|
|
||||||
case "update":
|
|
||||||
return {
|
|
||||||
label: "编辑",
|
|
||||||
hidden: !crud.getPermission("update"),
|
|
||||||
callback(_: any, done: Function) {
|
|
||||||
crud.rowEdit(row);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
case "delete":
|
|
||||||
return {
|
|
||||||
label: "删除",
|
|
||||||
hidden: !crud.getPermission("delete"),
|
|
||||||
callback(_: any, done: Function) {
|
|
||||||
crud.rowDelete(row);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
case "check":
|
|
||||||
return {
|
|
||||||
label: crud.selection.find((e: any) => e.id == row.id)
|
|
||||||
? "取消选择"
|
|
||||||
: "选择",
|
|
||||||
hidden: !props.columns.find((e: any) => e.type === "selection"),
|
|
||||||
callback(_: any, done: Function) {
|
|
||||||
toggleRowSelection(row);
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
case "order-desc":
|
|
||||||
return {
|
|
||||||
label: `${column.label} - 降序`,
|
|
||||||
hidden: !column.sortable,
|
|
||||||
callback(_: any, done: Function) {
|
|
||||||
changeSort(column.property, "desc");
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
case "order-asc":
|
|
||||||
return {
|
|
||||||
label: `${column.label} - 升序`,
|
|
||||||
hidden: !column.sortable,
|
|
||||||
callback(_: any, done: Function) {
|
|
||||||
changeSort(column.property, "asc");
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
if (isFunction(e)) {
|
|
||||||
return e(row, column, event);
|
|
||||||
} else {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.filter((e) => Boolean(e) && !e.hidden);
|
|
||||||
|
|
||||||
// 打开菜单
|
|
||||||
if (list.length > 0) {
|
|
||||||
ContextMenu.open(event, {
|
|
||||||
list
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 回调
|
|
||||||
if (props.props.onRowContextmenu) {
|
|
||||||
props.props.onRowContextmenu(row, column, event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算表格最大高度
|
|
||||||
function calcMaxHeight() {
|
|
||||||
if (!props.autoHeight) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
let vm: any = refs.value.table;
|
|
||||||
let h = 15;
|
|
||||||
|
|
||||||
// 获取表格元素
|
|
||||||
while (!vm.$parent?.$el.className.includes("cl-crud")) {
|
|
||||||
vm = vm.$parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取表格上的高度
|
|
||||||
h += vm.$el.offsetTop;
|
|
||||||
|
|
||||||
// 获取表格下的高度
|
|
||||||
let n = vm.$el.nextSibling;
|
|
||||||
|
|
||||||
while (n && (n.className || "").includes("el-row")) {
|
|
||||||
h += n.clientHeight + 5;
|
|
||||||
n = n.nextSibling;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置表格最大高度
|
|
||||||
maxHeight.value = crud.crudRef.clientHeight - h;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 显示列
|
|
||||||
function showColumn(prop: string | string[], status?: boolean) {
|
|
||||||
const keys = isArray(prop) ? prop : [prop];
|
|
||||||
visible.value = false;
|
|
||||||
|
|
||||||
props.columns
|
|
||||||
.filter((e) => (e.prop ? keys.includes(e.prop) : false))
|
|
||||||
.forEach((e) => {
|
|
||||||
e.hidden = isBoolean(status) ? status : false;
|
|
||||||
});
|
|
||||||
|
|
||||||
nextTick(() => {
|
|
||||||
visible.value = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 隐藏列
|
|
||||||
function hiddenColumn(prop: string | string[]) {
|
|
||||||
showColumn(prop, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 监听事件
|
|
||||||
(function () {
|
|
||||||
// 刷新事件
|
|
||||||
mitt.on("crud.refresh", ({ list }: any) => {
|
|
||||||
data.value = list;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 窗口大小改变事件
|
|
||||||
mitt.on("crud.resize", () => {
|
|
||||||
calcMaxHeight();
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
// 设置请求参数
|
|
||||||
(function () {
|
|
||||||
const { order, prop } = props.props["default-sort"] || {};
|
|
||||||
|
|
||||||
if (order && prop) {
|
|
||||||
crud.params.order = order === "descending" ? "desc" : "asc";
|
|
||||||
crud.params.prop = prop;
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
|
|
||||||
onMounted(function () {
|
|
||||||
calcMaxHeight();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
refs,
|
|
||||||
visible,
|
|
||||||
data,
|
|
||||||
maxHeight,
|
|
||||||
setRefs,
|
|
||||||
showColumn,
|
|
||||||
hiddenColumn,
|
|
||||||
onSelectionChange,
|
|
||||||
onSortChange,
|
|
||||||
onRowContextMenu,
|
|
||||||
clearSelection,
|
|
||||||
toggleRowSelection,
|
|
||||||
toggleAllSelection,
|
|
||||||
toggleRowExpansion,
|
|
||||||
setCurrentRow,
|
|
||||||
clearSort,
|
|
||||||
clearFilter,
|
|
||||||
doLayout,
|
|
||||||
sort
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
const browser = inject("browser") as Browser;
|
|
||||||
|
|
||||||
// 渲染列
|
|
||||||
const renderColumn = () => {
|
|
||||||
return ctx.columns
|
|
||||||
.filter((e: any) => !e.hidden)
|
|
||||||
.map((item: any, index: number) => {
|
|
||||||
const ElTableColumn = (
|
|
||||||
<el-table-column
|
|
||||||
key={`crud-table-column-${index}`}
|
|
||||||
align="center"></el-table-column>
|
|
||||||
);
|
|
||||||
|
|
||||||
// 操作按钮
|
|
||||||
if (item.type === "op") {
|
|
||||||
return h(
|
|
||||||
ElTableColumn,
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
width: "160px",
|
|
||||||
fixed: browser.isMini ? null : "right",
|
|
||||||
...item
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default: (scope: any) => {
|
|
||||||
return (
|
|
||||||
<div class="cl-table__op">
|
|
||||||
{(item.buttons || ["edit", "delete"]).map(
|
|
||||||
(vnode: any) => {
|
|
||||||
if (vnode === "edit") {
|
|
||||||
return (
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="text"
|
|
||||||
v-show={crud.getPermission(
|
|
||||||
"update"
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
|
||||||
crud.rowEdit(scope.row);
|
|
||||||
}}>
|
|
||||||
{crud.dict.label.update}
|
|
||||||
</el-button>
|
|
||||||
);
|
|
||||||
} else if (vnode === "delete") {
|
|
||||||
return (
|
|
||||||
<el-button
|
|
||||||
size="mini"
|
|
||||||
type="text"
|
|
||||||
v-show={crud.getPermission(
|
|
||||||
"delete"
|
|
||||||
)}
|
|
||||||
onClick={() => {
|
|
||||||
crud.rowDelete(scope.row);
|
|
||||||
}}>
|
|
||||||
{crud.dict.label.delete}
|
|
||||||
</el-button>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return renderNode(vnode, {
|
|
||||||
scope,
|
|
||||||
slots: ctx.$slots
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 多选,序号
|
|
||||||
else if (["selection", "index"].includes(item.type)) {
|
|
||||||
return h(ElTableColumn, item);
|
|
||||||
}
|
|
||||||
// 默认
|
|
||||||
else {
|
|
||||||
const deep = (item: any) => {
|
|
||||||
const props = cloneDeep(item);
|
|
||||||
|
|
||||||
// Cannot set property children of #<Element>
|
|
||||||
delete props.children;
|
|
||||||
|
|
||||||
return h(ElTableColumn, props, {
|
|
||||||
header: (scope: any) => {
|
|
||||||
const slot = ctx.$slots[`header-${item.prop}`];
|
|
||||||
|
|
||||||
if (slot) {
|
|
||||||
return slot({
|
|
||||||
scope
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return scope.column.label;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
default: (scope: any) => {
|
|
||||||
if (item.children) {
|
|
||||||
return <div>{item.children.map(deep)}</div>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scope data
|
|
||||||
const newScope = {
|
|
||||||
...scope,
|
|
||||||
...item
|
|
||||||
};
|
|
||||||
|
|
||||||
// 绑定值
|
|
||||||
const value = scope.row[item.prop];
|
|
||||||
|
|
||||||
// 使用插槽
|
|
||||||
const slot = ctx.$slots[`column-${item.prop}`];
|
|
||||||
|
|
||||||
if (slot) {
|
|
||||||
return slot({
|
|
||||||
scope: newScope
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// 判断是否自定义渲染
|
|
||||||
if (item.component) {
|
|
||||||
return renderNode(item.component, {
|
|
||||||
prop: item.prop,
|
|
||||||
scope: newScope.row
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Formatter
|
|
||||||
else if (item.formatter) {
|
|
||||||
return item.formatter(
|
|
||||||
newScope.row,
|
|
||||||
newScope.column,
|
|
||||||
newScope.row[item.prop],
|
|
||||||
newScope.$index
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 字典状态
|
|
||||||
else if (item.dict) {
|
|
||||||
const data = item.dict.find(
|
|
||||||
(d: any) => d.value == value
|
|
||||||
);
|
|
||||||
|
|
||||||
if (data) {
|
|
||||||
const ElTag = (
|
|
||||||
<el-tag
|
|
||||||
disable-transitions
|
|
||||||
size="small"
|
|
||||||
effect="dark"></el-tag>
|
|
||||||
);
|
|
||||||
|
|
||||||
// Use el-tag
|
|
||||||
return h(ElTag, data, {
|
|
||||||
default() {
|
|
||||||
return data.label;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Empty text
|
|
||||||
else if (isNull(value)) {
|
|
||||||
return scope.emptyText;
|
|
||||||
}
|
|
||||||
// Value
|
|
||||||
else {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return deep(item);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const ElTable = (
|
|
||||||
<el-table
|
|
||||||
class="cl-table"
|
|
||||||
ref={ctx.setRefs("table")}
|
|
||||||
border
|
|
||||||
size="mini"
|
|
||||||
v-loading={crud.loading}
|
|
||||||
data={ctx.data}></el-table>
|
|
||||||
);
|
|
||||||
|
|
||||||
return ctx.visible
|
|
||||||
? h(
|
|
||||||
ElTable,
|
|
||||||
{
|
|
||||||
onSortChange: ctx.onSortChange,
|
|
||||||
maxHeight: ctx.autoHeight ? ctx.maxHeight : null,
|
|
||||||
...ctx.props,
|
|
||||||
onSelectionChange: ctx.onSelectionChange,
|
|
||||||
onRowContextmenu: ctx.onRowContextMenu
|
|
||||||
},
|
|
||||||
{
|
|
||||||
default() {
|
|
||||||
return renderColumn();
|
|
||||||
},
|
|
||||||
empty() {
|
|
||||||
return (
|
|
||||||
<div class="cl-table__empty">
|
|
||||||
{ctx.$slots.empty && ctx.$slots.empty()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
append() {
|
|
||||||
return (
|
|
||||||
<div class="cl-table__append">
|
|
||||||
{ctx.$slots.append && ctx.$slots.append()}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
: null;
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,26 +0,0 @@
|
|||||||
export function useFormApi({ refs }: any) {
|
|
||||||
const apis: any = {};
|
|
||||||
|
|
||||||
[
|
|
||||||
"showLoading",
|
|
||||||
"hiddenLoading",
|
|
||||||
"collapseItem",
|
|
||||||
"getForm",
|
|
||||||
"setForm",
|
|
||||||
"setData",
|
|
||||||
"setOptions",
|
|
||||||
"toggleItem",
|
|
||||||
"hiddenItem",
|
|
||||||
"showItem",
|
|
||||||
"resetFields",
|
|
||||||
"clearValidate",
|
|
||||||
"validateField",
|
|
||||||
"validate"
|
|
||||||
].forEach((e) => {
|
|
||||||
apis[e] = (...args: any[]) => {
|
|
||||||
return refs.value.form[e](...args);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
return apis;
|
|
||||||
}
|
|
@ -1,293 +0,0 @@
|
|||||||
import { ElMessage } from "element-plus";
|
|
||||||
import { defineComponent, h, inject, ref } from "vue";
|
|
||||||
import type { PropType } from "vue";
|
|
||||||
import { useFormApi } from "./helper";
|
|
||||||
import { useForm, useRefs } from "../../hooks/core";
|
|
||||||
import { Crud, UpsertItem } from "../../types";
|
|
||||||
|
|
||||||
export default defineComponent({
|
|
||||||
name: "cl-upsert",
|
|
||||||
|
|
||||||
props: {
|
|
||||||
// 绑定值
|
|
||||||
modelValue: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 表单项
|
|
||||||
items: {
|
|
||||||
type: Array as PropType<UpsertItem[]>,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
// el-form 参数
|
|
||||||
props: {
|
|
||||||
type: Object,
|
|
||||||
default: () => {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 编辑时是否同步打开
|
|
||||||
sync: Boolean,
|
|
||||||
// 操作按钮参数 { hidden, saveButtonText, closeButtonText, buttons }
|
|
||||||
op: Object,
|
|
||||||
// cl-dialog 参数 { props, hiddenControls, controls }
|
|
||||||
dialog: Object,
|
|
||||||
// 打开表单钩子 { isEdit, data, { submit, done, close } }
|
|
||||||
onOpen: Function,
|
|
||||||
// 关闭表单钩子 { done }
|
|
||||||
onClose: Function,
|
|
||||||
// 获取表单数据钩子 { data, { next, done, close } }
|
|
||||||
onInfo: Function,
|
|
||||||
// 表单提交钩子 { isEdit, data, { next, done, close } }
|
|
||||||
onSubmit: Function
|
|
||||||
},
|
|
||||||
|
|
||||||
emits: ["open", "close"],
|
|
||||||
|
|
||||||
setup(props, { emit }) {
|
|
||||||
const { refs, setRefs }: any = useRefs();
|
|
||||||
const { setFormData } = useForm(props);
|
|
||||||
|
|
||||||
// 参数注入
|
|
||||||
const mitt = inject<any>("mitt");
|
|
||||||
const crud = inject("crud") as Crud;
|
|
||||||
|
|
||||||
// 表单数据
|
|
||||||
const form: any = setFormData();
|
|
||||||
// 是否编辑
|
|
||||||
const isEdit = ref<boolean>(false);
|
|
||||||
|
|
||||||
// 关闭表单
|
|
||||||
function close() {
|
|
||||||
refs.value.form.close();
|
|
||||||
emit("close");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 表单关闭前
|
|
||||||
function beforeClose() {
|
|
||||||
if (props.onClose) {
|
|
||||||
props.onClose(close);
|
|
||||||
} else {
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交
|
|
||||||
function submit(data: any, event?: any) {
|
|
||||||
function done() {
|
|
||||||
return event ? event.done() : refs.value.form.done();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function next(data: any) {
|
|
||||||
// 获取请求方法
|
|
||||||
const reqName: string = crud.dict.api[isEdit.value ? "update" : "add"];
|
|
||||||
|
|
||||||
// 验证请求
|
|
||||||
if (!crud.service[reqName]) {
|
|
||||||
done();
|
|
||||||
return Promise.reject(`Request function '${reqName}' is not fount!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送请求
|
|
||||||
await crud.service[reqName](data)
|
|
||||||
.then((res: any) => {
|
|
||||||
ElMessage.success("保存成功");
|
|
||||||
close();
|
|
||||||
crud.refresh();
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.catch((err: string) => {
|
|
||||||
ElMessage.error(err);
|
|
||||||
Promise.reject(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 请求完成
|
|
||||||
done();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 提交钩子
|
|
||||||
if (props.onSubmit) {
|
|
||||||
props.onSubmit(isEdit.value, data, {
|
|
||||||
done,
|
|
||||||
next,
|
|
||||||
close
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
next(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打开表单
|
|
||||||
function open() {
|
|
||||||
return new Promise((resolve) => {
|
|
||||||
if (!refs.value.form) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
refs.value.form.open({
|
|
||||||
title: isEdit.value ? "编辑" : "新增",
|
|
||||||
props: props.props,
|
|
||||||
items: props.items,
|
|
||||||
op: props.op,
|
|
||||||
dialog: props.dialog,
|
|
||||||
on: {
|
|
||||||
open: (_: any, { done, close }: any) => {
|
|
||||||
if (props.onOpen) {
|
|
||||||
props.onOpen(isEdit, form, {
|
|
||||||
submit: () => {
|
|
||||||
submit(form);
|
|
||||||
},
|
|
||||||
done,
|
|
||||||
close
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
resolve(true);
|
|
||||||
},
|
|
||||||
submit,
|
|
||||||
close: beforeClose
|
|
||||||
},
|
|
||||||
_data: {
|
|
||||||
isEdit: isEdit
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 新增
|
|
||||||
async function add() {
|
|
||||||
isEdit.value = false;
|
|
||||||
await open();
|
|
||||||
emit("open", false, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 追加
|
|
||||||
async function append(data: any) {
|
|
||||||
isEdit.value = false;
|
|
||||||
await open();
|
|
||||||
if (data) {
|
|
||||||
Object.assign(form, data);
|
|
||||||
}
|
|
||||||
emit("open", false, form);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑
|
|
||||||
function edit(data?: any) {
|
|
||||||
if (!refs.value.form) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const { showLoading, hiddenLoading } = refs.value.form;
|
|
||||||
|
|
||||||
// 设置为编辑
|
|
||||||
isEdit.value = true;
|
|
||||||
|
|
||||||
// 显示加载中
|
|
||||||
showLoading();
|
|
||||||
|
|
||||||
// 是否同步打开
|
|
||||||
if (!props.sync) {
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
|
|
||||||
const done = (data?: any) => {
|
|
||||||
// 加载完成
|
|
||||||
hiddenLoading();
|
|
||||||
|
|
||||||
// 合并数据
|
|
||||||
if (data) {
|
|
||||||
refs.value.form.reBindForm(data);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 关闭表单
|
|
||||||
const close = () => {
|
|
||||||
hiddenLoading();
|
|
||||||
close();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取详情
|
|
||||||
const next = async (data: any) => {
|
|
||||||
// 获取请求名称
|
|
||||||
const reqName: any = crud.dict.api.info;
|
|
||||||
|
|
||||||
// 验证请求
|
|
||||||
if (!crud.service[reqName]) {
|
|
||||||
done();
|
|
||||||
return Promise.reject(`Request function '${reqName}' is not fount!`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 发送请求
|
|
||||||
await crud.service[reqName]({
|
|
||||||
id: data.id
|
|
||||||
})
|
|
||||||
.then((res: any) => {
|
|
||||||
done(res);
|
|
||||||
|
|
||||||
// 同步打开表单
|
|
||||||
if (props.sync) {
|
|
||||||
open();
|
|
||||||
}
|
|
||||||
|
|
||||||
emit("open", isEdit.value, form);
|
|
||||||
return res;
|
|
||||||
})
|
|
||||||
.catch((err: string) => {
|
|
||||||
ElMessage.error(err);
|
|
||||||
return Promise.reject(err);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 隐藏加载框
|
|
||||||
hiddenLoading();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 获取详情钩子
|
|
||||||
if (props.onInfo) {
|
|
||||||
props.onInfo(data, {
|
|
||||||
next,
|
|
||||||
done: (data: any) => {
|
|
||||||
done(data);
|
|
||||||
emit("open", true, form);
|
|
||||||
},
|
|
||||||
close
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
next(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 消息事件
|
|
||||||
(function () {
|
|
||||||
mitt.on("crud.add", add);
|
|
||||||
mitt.on("crud.append", append);
|
|
||||||
mitt.on("crud.edit", edit);
|
|
||||||
mitt.on("crud.close", close);
|
|
||||||
})();
|
|
||||||
|
|
||||||
return {
|
|
||||||
refs,
|
|
||||||
setRefs,
|
|
||||||
form,
|
|
||||||
isEdit,
|
|
||||||
add,
|
|
||||||
append,
|
|
||||||
edit,
|
|
||||||
close,
|
|
||||||
...useFormApi({ refs })
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
render(ctx: any) {
|
|
||||||
return (
|
|
||||||
<div class="cl-upsert">
|
|
||||||
{h(
|
|
||||||
<cl-form ref={ctx.setRefs("form")} v-model={ctx.form}></cl-form>,
|
|
||||||
{},
|
|
||||||
ctx.$slots
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
@ -1,41 +0,0 @@
|
|||||||
import { ref, onBeforeUpdate, watch, reactive } from "vue";
|
|
||||||
import { isEmpty } from "../utils";
|
|
||||||
|
|
||||||
export function useForm(props: any) {
|
|
||||||
const setFormData = () => {
|
|
||||||
const data = reactive<any>(props.modelValue || {});
|
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.modelValue,
|
|
||||||
(val: any) => {
|
|
||||||
for (const i in data) {
|
|
||||||
if (isEmpty(val[i])) {
|
|
||||||
delete data[i];
|
|
||||||
} else {
|
|
||||||
data[i] = val[i];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return data;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
setFormData
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useRefs() {
|
|
||||||
const refs = ref<HTMLElement[]>([]);
|
|
||||||
|
|
||||||
onBeforeUpdate(() => {
|
|
||||||
refs.value = [];
|
|
||||||
});
|
|
||||||
|
|
||||||
const setRefs = (index: number) => (el: HTMLElement) => {
|
|
||||||
refs.value[index] = el;
|
|
||||||
};
|
|
||||||
|
|
||||||
return { refs, setRefs };
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
import { isArray, isFunction, isObject, isString } from "../utils";
|
|
||||||
|
|
||||||
export const format: any = {
|
|
||||||
number(value: any) {
|
|
||||||
return isArray(value) ? value.map(Number) : Number(value);
|
|
||||||
},
|
|
||||||
string(value: any) {
|
|
||||||
return isArray(value) ? value.map(String) : String(value);
|
|
||||||
},
|
|
||||||
split(value: any, separator = ",") {
|
|
||||||
return value.split(separator);
|
|
||||||
},
|
|
||||||
join(value: any, separator = ",") {
|
|
||||||
return value.join(separator);
|
|
||||||
},
|
|
||||||
boolean(value: any) {
|
|
||||||
return Boolean(value);
|
|
||||||
},
|
|
||||||
booleanNumber(value: any) {
|
|
||||||
return Boolean(value) ? 1 : 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function parse(method: string, { value, pipe, form }: any) {
|
|
||||||
if (value === undefined) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pipe) {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
let pipes = [];
|
|
||||||
|
|
||||||
if (isString(pipe)) {
|
|
||||||
if (format[pipe]) {
|
|
||||||
pipes = [pipe];
|
|
||||||
} else {
|
|
||||||
console.error(`${pipe} is not found.`);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
} else if (isArray(pipe)) {
|
|
||||||
pipes = pipe;
|
|
||||||
} else if (isObject(pipe)) {
|
|
||||||
pipes = isArray(pipe[method]) ? pipe[method] : [pipe[method]];
|
|
||||||
} else if (isFunction(pipe)) {
|
|
||||||
pipes = [pipe];
|
|
||||||
} else {
|
|
||||||
console.error(`Hook data error!`);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
let d = value;
|
|
||||||
|
|
||||||
pipes.forEach((e: any) => {
|
|
||||||
if (isString(e)) {
|
|
||||||
d = format[e](d);
|
|
||||||
} else if (isFunction(e)) {
|
|
||||||
d = e(d, form);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
bind(value: any, pipe: any, form: any) {
|
|
||||||
return parse("bind", { value, pipe, form });
|
|
||||||
},
|
|
||||||
|
|
||||||
submit(value: any, pipe: any, form: any) {
|
|
||||||
return parse("submit", { value, pipe, form });
|
|
||||||
}
|
|
||||||
};
|
|
@ -1,58 +0,0 @@
|
|||||||
import { reactive } from "vue";
|
|
||||||
import * as components from "./components";
|
|
||||||
import ContextMenu from "./components/context-menu/index";
|
|
||||||
|
|
||||||
import "./static/index.scss";
|
|
||||||
|
|
||||||
const CRUD = {
|
|
||||||
install(app: any, options: any = {}) {
|
|
||||||
app.provide("__crud", options.crud);
|
|
||||||
|
|
||||||
// 获取浏览器信息
|
|
||||||
(function () {
|
|
||||||
const browser = reactive<any>({
|
|
||||||
isMini: false,
|
|
||||||
screen: "full"
|
|
||||||
});
|
|
||||||
|
|
||||||
function resize() {
|
|
||||||
const w = document.body.clientWidth;
|
|
||||||
|
|
||||||
if (w < 768) {
|
|
||||||
browser.screen = "xs";
|
|
||||||
} else if (w < 992) {
|
|
||||||
browser.screen = "sm";
|
|
||||||
} else if (w < 1200) {
|
|
||||||
browser.screen = "md";
|
|
||||||
} else if (w < 1920) {
|
|
||||||
browser.screen = "xl";
|
|
||||||
} else {
|
|
||||||
browser.screen = "full";
|
|
||||||
}
|
|
||||||
|
|
||||||
browser.isMini = browser.screen === "xs";
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("resize", resize);
|
|
||||||
resize();
|
|
||||||
|
|
||||||
app.provide("browser", browser);
|
|
||||||
})();
|
|
||||||
|
|
||||||
// 设置组件
|
|
||||||
for (const i in components) {
|
|
||||||
// @ts-ignore
|
|
||||||
app.component(components[i].name, components[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
app.config.globalProperties.$crud = {
|
|
||||||
openContextMenu: ContextMenu.open
|
|
||||||
};
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export default CRUD;
|
|
||||||
|
|
||||||
export { ContextMenu, CRUD };
|
|
@ -1,596 +0,0 @@
|
|||||||
.cl-crud {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
position: relative;
|
|
||||||
padding: 10px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
background-color: #fff;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&.is-border {
|
|
||||||
border: 1px solid #eee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-input-number {
|
|
||||||
&__decrease,
|
|
||||||
&__increase {
|
|
||||||
border: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
& > .el-row {
|
|
||||||
overflow-x: auto;
|
|
||||||
overflow-y: hidden;
|
|
||||||
flex-wrap: nowrap;
|
|
||||||
padding-bottom: 5px;
|
|
||||||
margin-bottom: 0;
|
|
||||||
min-height: 33px;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
height: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
background-color: rgba(144, 147, 153, 0.3);
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
& + .el-row {
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-flex1 {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-search-key {
|
|
||||||
display: flex;
|
|
||||||
margin-left: 10px;
|
|
||||||
|
|
||||||
&__select {
|
|
||||||
margin-right: 10px;
|
|
||||||
|
|
||||||
.el-input__inner {
|
|
||||||
width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__input {
|
|
||||||
.el-input__inner {
|
|
||||||
width: 250px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__button {
|
|
||||||
&.el-button {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-adv-btn {
|
|
||||||
& > .el-button {
|
|
||||||
margin-left: 10px;
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-table {
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.el-table {
|
|
||||||
.el-loading-mask {
|
|
||||||
.el-loading-spinner {
|
|
||||||
.el-icon-loading {
|
|
||||||
font-size: 25px;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-loading-text {
|
|
||||||
color: #666;
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.el-loading-parent--relative {
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-crud__op-dropdown-menu {
|
|
||||||
.el-dropdown-menu__item {
|
|
||||||
height: 30px;
|
|
||||||
line-height: 30px;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-button {
|
|
||||||
height: 30px;
|
|
||||||
width: 100%;
|
|
||||||
padding: 0 20px;
|
|
||||||
text-align: left;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-query {
|
|
||||||
display: inline-flex;
|
|
||||||
margin: 0 10px;
|
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
button {
|
|
||||||
border: 0;
|
|
||||||
background-color: #fff;
|
|
||||||
font-size: 12px;
|
|
||||||
outline: none;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #666;
|
|
||||||
white-space: nowrap;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #6fa8ff;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active {
|
|
||||||
color: #409eff;
|
|
||||||
}
|
|
||||||
|
|
||||||
span {
|
|
||||||
display: inline-block;
|
|
||||||
padding: 0 15px;
|
|
||||||
border-right: 1px solid #ddd;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
span {
|
|
||||||
border: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-filter {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
margin: 0 10px;
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
font-size: 12px;
|
|
||||||
margin-right: 10px;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-select {
|
|
||||||
min-width: 120px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-filter-group {
|
|
||||||
display: inline-flex;
|
|
||||||
white-space: nowrap;
|
|
||||||
margin: 0 10px;
|
|
||||||
|
|
||||||
&__items {
|
|
||||||
display: inline-flex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-adv-search {
|
|
||||||
.el-drawer {
|
|
||||||
&__header {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__body {
|
|
||||||
height: calc(100% - 63px);
|
|
||||||
padding: 0 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__container {
|
|
||||||
height: calc(100% - 55px);
|
|
||||||
overflow-y: auto;
|
|
||||||
padding: 10px 20px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&::-webkit-scrollbar-thumb {
|
|
||||||
border-radius: 6px;
|
|
||||||
background-color: rgba(144, 147, 153, 0.3);
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-form-item__content {
|
|
||||||
& > div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: flex-end;
|
|
||||||
height: 50px;
|
|
||||||
border-top: 1px solid #ebeef5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-drawer {
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
&__header {
|
|
||||||
span {
|
|
||||||
outline: none;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__close-btn {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-form {
|
|
||||||
.el-form-item {
|
|
||||||
.el-input-number {
|
|
||||||
&__decrease,
|
|
||||||
&__increase {
|
|
||||||
border: 0;
|
|
||||||
background-color: transparent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__label {
|
|
||||||
.el-tooltip {
|
|
||||||
i {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-item {
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
&__prepend {
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__component {
|
|
||||||
&.is-flex {
|
|
||||||
flex: 1;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__append {
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__collapse {
|
|
||||||
width: 100%;
|
|
||||||
font-size: 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.el-divider {
|
|
||||||
margin: 16px 0;
|
|
||||||
|
|
||||||
&__text {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
margin-left: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-form-tabs {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border-bottom: 1px solid #ccc;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
ul {
|
|
||||||
height: 35px;
|
|
||||||
width: 100%;
|
|
||||||
overflow-x: auto;
|
|
||||||
white-space: nowrap;
|
|
||||||
position: relative;
|
|
||||||
scrollbar-width: none;
|
|
||||||
-ms-overflow-style: none;
|
|
||||||
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
list-style: none;
|
|
||||||
padding: 0 20px;
|
|
||||||
height: 35px;
|
|
||||||
line-height: 35px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__line {
|
|
||||||
height: 2px;
|
|
||||||
width: 100%;
|
|
||||||
position: absolute;
|
|
||||||
bottom: -1px;
|
|
||||||
left: 0;
|
|
||||||
transition: transform 0.3s ease-in-out, width 0.2s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-dialog {
|
|
||||||
margin-top: 0px !important;
|
|
||||||
|
|
||||||
.el-dialog {
|
|
||||||
&__header {
|
|
||||||
padding: 10px !important;
|
|
||||||
text-align: center;
|
|
||||||
border-bottom: 1px solid #f7f7f7;
|
|
||||||
|
|
||||||
.el-dialog__title {
|
|
||||||
font-size: 15px;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-dialog__headerbtn {
|
|
||||||
display: none;
|
|
||||||
top: 13px;
|
|
||||||
|
|
||||||
.el-dialog__close {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-slot {
|
|
||||||
&.is-drag {
|
|
||||||
-moz-user-select: none;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-ms-user-select: none;
|
|
||||||
-khtml-user-select: none;
|
|
||||||
user-select: none;
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__body {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__footer {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__header {
|
|
||||||
height: 25px;
|
|
||||||
line-height: 25px;
|
|
||||||
text-align: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__title {
|
|
||||||
display: block;
|
|
||||||
font-size: 15px;
|
|
||||||
letter-spacing: 0.5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__controls {
|
|
||||||
display: flex;
|
|
||||||
justify-content: flex-end;
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
top: 0;
|
|
||||||
z-index: 9;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
button,
|
|
||||||
.minimize,
|
|
||||||
.maximize,
|
|
||||||
.close {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
height: 25px;
|
|
||||||
width: 40px;
|
|
||||||
border: 0;
|
|
||||||
background-color: #fff;
|
|
||||||
cursor: pointer;
|
|
||||||
outline: none;
|
|
||||||
|
|
||||||
i {
|
|
||||||
font-size: 16px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.hidden-header {
|
|
||||||
.el-dialog__header {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.cl-context-menu {
|
|
||||||
position: absolute;
|
|
||||||
z-index: 9999;
|
|
||||||
|
|
||||||
&__box {
|
|
||||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
|
||||||
width: 160px;
|
|
||||||
padding: 5px 0;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 3px;
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
|
|
||||||
&.is-append {
|
|
||||||
right: calc(-100% - 5px);
|
|
||||||
top: -5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
& > div {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
height: 35px;
|
|
||||||
font-size: 13px;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 0 15px;
|
|
||||||
color: #666;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
span {
|
|
||||||
height: 35px;
|
|
||||||
line-height: 35px;
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
&:first-child {
|
|
||||||
margin-right: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:last-child {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-active {
|
|
||||||
background-color: #f7f7f7;
|
|
||||||
color: #000;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-ellipsis {
|
|
||||||
span {
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.is-disabled {
|
|
||||||
span {
|
|
||||||
color: #ccc;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: #ccc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Element-ui Theme
|
|
||||||
.el-message {
|
|
||||||
&.el-message--success,
|
|
||||||
&.el-message--error,
|
|
||||||
&.el-message--info,
|
|
||||||
&.el-message--warning {
|
|
||||||
min-width: auto;
|
|
||||||
background-color: #fff;
|
|
||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
||||||
border: 0;
|
|
||||||
padding: 12px 20px 12px 15px;
|
|
||||||
|
|
||||||
.el-message__content {
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-table {
|
|
||||||
table {
|
|
||||||
th {
|
|
||||||
background-color: #ebeef5;
|
|
||||||
padding: 3px 0;
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cell {
|
|
||||||
color: rgba(0, 0, 0, 0.85);
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&__column {
|
|
||||||
&-filter-trigger {
|
|
||||||
margin-left: 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&-column--selection {
|
|
||||||
.cell {
|
|
||||||
padding: 0 14px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-table-filter {
|
|
||||||
margin-top: 5px !important;
|
|
||||||
|
|
||||||
.el-checkbox__label {
|
|
||||||
font-size: 12px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 768px) {
|
|
||||||
.el-message-box {
|
|
||||||
width: 90% !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.el-table {
|
|
||||||
&__body {
|
|
||||||
&-wrapper {
|
|
||||||
&::-webkit-scrollbar {
|
|
||||||
height: 6px;
|
|
||||||
width: 6px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
3
src/cool/modules/crud/types/adv-search.d.ts
vendored
3
src/cool/modules/crud/types/adv-search.d.ts
vendored
@ -1,3 +0,0 @@
|
|||||||
import { FormItem } from "./form";
|
|
||||||
|
|
||||||
export declare type AdvSearchItem = FormItem;
|
|
4
src/cool/modules/crud/types/browser.d.ts
vendored
4
src/cool/modules/crud/types/browser.d.ts
vendored
@ -1,4 +0,0 @@
|
|||||||
export declare type Browser = {
|
|
||||||
screen: string;
|
|
||||||
isMini: boolean;
|
|
||||||
};
|
|
15
src/cool/modules/crud/types/context-menu.d.ts
vendored
15
src/cool/modules/crud/types/context-menu.d.ts
vendored
@ -1,15 +0,0 @@
|
|||||||
export declare interface ContextMenuItem {
|
|
||||||
label: string;
|
|
||||||
"prefix-icon"?: string;
|
|
||||||
"suffix-icon"?: string;
|
|
||||||
ellipsis?: boolean;
|
|
||||||
disabled?: boolean;
|
|
||||||
hidden?: boolean;
|
|
||||||
children?: Array<ContextMenuItem>;
|
|
||||||
showChildren?: boolean;
|
|
||||||
callback?(item: ContextMenuItem, done: Function): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface ContextMenuOptions {
|
|
||||||
list?: ContextMenuItem[];
|
|
||||||
}
|
|
108
src/cool/modules/crud/types/crud.d.ts
vendored
108
src/cool/modules/crud/types/crud.d.ts
vendored
@ -1,108 +0,0 @@
|
|||||||
import { TableOptions } from "./table";
|
|
||||||
|
|
||||||
export declare type ServiceName = "page" | "list" | "add" | "delete" | "update" | "info" | string;
|
|
||||||
|
|
||||||
export declare interface Service {
|
|
||||||
page?(
|
|
||||||
params?: any
|
|
||||||
): Promise<{
|
|
||||||
list: any[];
|
|
||||||
pagination?: {
|
|
||||||
total?: number;
|
|
||||||
page?: number;
|
|
||||||
size?: number;
|
|
||||||
};
|
|
||||||
}>;
|
|
||||||
list?(params?: any): Promise<any[]>;
|
|
||||||
add?(params: any): Promise<any>;
|
|
||||||
delete?(params: any): Promise<any>;
|
|
||||||
update?(params: any): Promise<any>;
|
|
||||||
info?(params: any): Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface Dict {
|
|
||||||
api: {
|
|
||||||
list: string;
|
|
||||||
add: string;
|
|
||||||
update: string;
|
|
||||||
delete: string;
|
|
||||||
info: string;
|
|
||||||
page: string;
|
|
||||||
};
|
|
||||||
pagination: {
|
|
||||||
page: string;
|
|
||||||
size: string;
|
|
||||||
};
|
|
||||||
search: {
|
|
||||||
keyWord: string;
|
|
||||||
query: string;
|
|
||||||
};
|
|
||||||
sort: {
|
|
||||||
order: string;
|
|
||||||
prop: string;
|
|
||||||
};
|
|
||||||
label: {
|
|
||||||
add: string;
|
|
||||||
delete: string;
|
|
||||||
multiDelete: string;
|
|
||||||
update: string;
|
|
||||||
refresh: string;
|
|
||||||
advSearch: string;
|
|
||||||
saveButtonText: string;
|
|
||||||
closeButtonText: string;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface Permission {
|
|
||||||
page?: boolean;
|
|
||||||
list?: boolean;
|
|
||||||
add?: boolean;
|
|
||||||
delete?: boolean;
|
|
||||||
update?: boolean;
|
|
||||||
info?: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface LoadCtx {
|
|
||||||
service(s: Service): LoadCtx;
|
|
||||||
permission(p: Function | any): LoadCtx;
|
|
||||||
set(key: "dict" | "style", value: any): LoadCtx;
|
|
||||||
done(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface LoadApp {
|
|
||||||
refresh(params?: any): Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface CrudLoad {
|
|
||||||
app: LoadApp;
|
|
||||||
ctx: LoadCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface CrudRef {
|
|
||||||
getPermission(key?: string): boolean;
|
|
||||||
rowAdd(): any;
|
|
||||||
rowEdit(data: any): any;
|
|
||||||
rowAppend(data?: any): any;
|
|
||||||
rowClose(): any;
|
|
||||||
openAdvSearch(): any;
|
|
||||||
closeAdvSearch(): any;
|
|
||||||
rowDelete(...selection: any[]): void;
|
|
||||||
refresh(params?: any): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface Crud extends CrudRef {
|
|
||||||
crudRef: any;
|
|
||||||
permission: Permission;
|
|
||||||
service: any;
|
|
||||||
dict: Dict;
|
|
||||||
params: any;
|
|
||||||
table?: TableOptions;
|
|
||||||
selection: any[];
|
|
||||||
loading: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface Mitt {
|
|
||||||
on(name: string, ...args: any[]): void;
|
|
||||||
emit(name: string, ...args: any[]): void;
|
|
||||||
off(name: string, ...args: any[]): void;
|
|
||||||
}
|
|
84
src/cool/modules/crud/types/form.d.ts
vendored
84
src/cool/modules/crud/types/form.d.ts
vendored
@ -1,84 +0,0 @@
|
|||||||
import { FormHook } from "./hook";
|
|
||||||
import { RenderOptions } from "./render";
|
|
||||||
|
|
||||||
export declare interface FormItem {
|
|
||||||
type?: "tabs" | string;
|
|
||||||
prop?: string;
|
|
||||||
props?: {
|
|
||||||
labels?: Array<{ label: string; value: string }>;
|
|
||||||
labelWidth?: string;
|
|
||||||
error?: string;
|
|
||||||
showMessage?: boolean;
|
|
||||||
inlineMessage?: boolean;
|
|
||||||
size?: "medium" | "small" | "mini";
|
|
||||||
};
|
|
||||||
hook?: FormHook;
|
|
||||||
group?: string;
|
|
||||||
collapse?: boolean;
|
|
||||||
value?: any;
|
|
||||||
label?: string | { text?: string; icon?: string; tip?: string };
|
|
||||||
span?: number;
|
|
||||||
flex?: boolean;
|
|
||||||
hidden?: Function | boolean | string;
|
|
||||||
prepend?: RenderOptions;
|
|
||||||
component?: RenderOptions;
|
|
||||||
append?: RenderOptions;
|
|
||||||
rules?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface FormOpenEvent {
|
|
||||||
close(): void;
|
|
||||||
submit(): void;
|
|
||||||
done(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
declare interface FormSubmitEvent {
|
|
||||||
done(): void;
|
|
||||||
close(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface Form {
|
|
||||||
title?: string;
|
|
||||||
width?: string;
|
|
||||||
props?: any;
|
|
||||||
items: Array<FormItem>;
|
|
||||||
form?: any;
|
|
||||||
on?: {
|
|
||||||
open?(form: any, event: FormOpenEvent): void;
|
|
||||||
close?(done: Function): void;
|
|
||||||
submit?(data: any, event: FormSubmitEvent): void;
|
|
||||||
};
|
|
||||||
op?: {
|
|
||||||
hidden?: boolean;
|
|
||||||
saveButtonText?: string;
|
|
||||||
closeButtonText?: string;
|
|
||||||
buttons?: Array<"close" | "save">;
|
|
||||||
};
|
|
||||||
dialog?: {
|
|
||||||
props?: any;
|
|
||||||
hiddenControls?: boolean;
|
|
||||||
controls?: Array<"fullscreen" | "close">;
|
|
||||||
};
|
|
||||||
_data?: any;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface FormRef {
|
|
||||||
create(options: Form): FormRef;
|
|
||||||
open(options: Form): FormRef;
|
|
||||||
close(): void;
|
|
||||||
done(): void;
|
|
||||||
clear(): void;
|
|
||||||
showLoading(): void;
|
|
||||||
hiddenLoading(): void;
|
|
||||||
setData(): void;
|
|
||||||
setOptions(prop: string, list: Array<{ label: string; value?: any }>): void;
|
|
||||||
getForm(prop?: string): any;
|
|
||||||
setForm(prop: string, value: any): void;
|
|
||||||
toggleItem(prop: string, flag?: boolean): void;
|
|
||||||
hiddenItem(props: string[]): void;
|
|
||||||
showItem(props: string[]): void;
|
|
||||||
resetFields(): void;
|
|
||||||
clearValidate(props: string[] | string): void;
|
|
||||||
validateField(props: string[] | string, callback: Function): void;
|
|
||||||
validate(callback: Function): void;
|
|
||||||
}
|
|
@ -1,14 +0,0 @@
|
|||||||
export declare type Pipe =
|
|
||||||
| "number"
|
|
||||||
| "string"
|
|
||||||
| "split"
|
|
||||||
| "join"
|
|
||||||
| "boolean"
|
|
||||||
| "booleanNumber"
|
|
||||||
| Function
|
|
||||||
| Array<Pipe>;
|
|
||||||
|
|
||||||
export declare interface FormHook {
|
|
||||||
bind?: Pipe;
|
|
||||||
submit?: Pipe;
|
|
||||||
}
|
|
10
src/cool/modules/crud/types/index.d.ts
vendored
10
src/cool/modules/crud/types/index.d.ts
vendored
@ -1,10 +0,0 @@
|
|||||||
export * from "./crud";
|
|
||||||
export * from "./table";
|
|
||||||
export * from "./context-menu";
|
|
||||||
export * from "./form";
|
|
||||||
export * from "./upsert";
|
|
||||||
export * from "./adv-search";
|
|
||||||
export * from "./query";
|
|
||||||
export * from "./op";
|
|
||||||
export * from "./browser";
|
|
||||||
export * from "./hook";
|
|
111
src/cool/modules/crud/types/op.d.ts
vendored
111
src/cool/modules/crud/types/op.d.ts
vendored
@ -1,111 +0,0 @@
|
|||||||
export declare interface RefreshOp {
|
|
||||||
/**
|
|
||||||
* 渲染
|
|
||||||
* @param list 数据列表
|
|
||||||
* @param pagination 分页信息
|
|
||||||
*/
|
|
||||||
render(list: any[], pagination?: { size?: number; page?: number; total?: number }): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 继续执行刷新
|
|
||||||
* @param params 请求参数
|
|
||||||
*/
|
|
||||||
next(params?: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭加载状态
|
|
||||||
*/
|
|
||||||
done(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface DeleteOp {
|
|
||||||
next(params?: any): Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface UpsertOpenOp {
|
|
||||||
/**
|
|
||||||
* 提交表单
|
|
||||||
* @param form 表单值
|
|
||||||
*/
|
|
||||||
submit(form: any): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭加载状态
|
|
||||||
*/
|
|
||||||
done(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭弹窗
|
|
||||||
*/
|
|
||||||
close(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface UpsertCloseOp {
|
|
||||||
/**
|
|
||||||
* 关闭弹窗
|
|
||||||
*/
|
|
||||||
done(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface UpsertInfoOp {
|
|
||||||
/**
|
|
||||||
* 继续执行获取详情
|
|
||||||
* @param params 请求参数
|
|
||||||
*/
|
|
||||||
next(params: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭加载状态,并设置表单值
|
|
||||||
*/
|
|
||||||
done(data: any): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭弹窗
|
|
||||||
*/
|
|
||||||
close(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface UpsertSubmitOp {
|
|
||||||
/**
|
|
||||||
* 继续执行提交
|
|
||||||
* @param params 请求参数
|
|
||||||
*/
|
|
||||||
next(params: any): Promise<any>;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭加载状态
|
|
||||||
*/
|
|
||||||
done(): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 关闭弹窗
|
|
||||||
*/
|
|
||||||
close(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface AdvOpenOp {
|
|
||||||
/**
|
|
||||||
* 继续执行打开
|
|
||||||
* @param data 筛选参数
|
|
||||||
*/
|
|
||||||
next(data: any): Promise<any>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface AdvCloseOp {
|
|
||||||
/**
|
|
||||||
* 关闭抽屉
|
|
||||||
*/
|
|
||||||
done(): void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface AdvSearchOp {
|
|
||||||
/**
|
|
||||||
* 继续执行搜索
|
|
||||||
* @param params 请求参数
|
|
||||||
*/
|
|
||||||
next(params: any): Promise<any>;
|
|
||||||
/**
|
|
||||||
* 关闭抽屉
|
|
||||||
*/
|
|
||||||
done(): void;
|
|
||||||
}
|
|
4
src/cool/modules/crud/types/query.d.ts
vendored
4
src/cool/modules/crud/types/query.d.ts
vendored
@ -1,4 +0,0 @@
|
|||||||
export declare interface QueryList {
|
|
||||||
label: string;
|
|
||||||
value: any;
|
|
||||||
}
|
|
12
src/cool/modules/crud/types/render.d.ts
vendored
12
src/cool/modules/crud/types/render.d.ts
vendored
@ -1,12 +0,0 @@
|
|||||||
import { ComponentOptions } from "vue";
|
|
||||||
|
|
||||||
export declare interface Options extends ComponentOptions {
|
|
||||||
name: string;
|
|
||||||
|
|
||||||
options?: Array<{
|
|
||||||
label: string;
|
|
||||||
value?: any;
|
|
||||||
}>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare type RenderOptions = Options | Function;
|
|
66
src/cool/modules/crud/types/table.d.ts
vendored
66
src/cool/modules/crud/types/table.d.ts
vendored
@ -1,66 +0,0 @@
|
|||||||
import { ContextMenuItem } from "./context-menu";
|
|
||||||
import { RenderOptions } from "./render";
|
|
||||||
|
|
||||||
export declare interface TableOptions {
|
|
||||||
"context-menu"?:
|
|
||||||
| boolean
|
|
||||||
| Array<
|
|
||||||
| ContextMenuItem
|
|
||||||
| Function
|
|
||||||
| "refresh"
|
|
||||||
| "check"
|
|
||||||
| "update"
|
|
||||||
| "delete"
|
|
||||||
| "order-desc"
|
|
||||||
| "order-asc"
|
|
||||||
>;
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface TableColumn {
|
|
||||||
value?: any;
|
|
||||||
type?: "index" | "selection" | "expand" | "op";
|
|
||||||
hidden?: boolean | (({ scope }: any) => boolean);
|
|
||||||
component?: RenderOptions;
|
|
||||||
dict?: Array<{
|
|
||||||
label: string;
|
|
||||||
value?: any;
|
|
||||||
type?: "primary" | "success" | "warning" | "info" | "danger";
|
|
||||||
}>;
|
|
||||||
buttons?: Array<"edit" | "delete" | string | RenderOptions>;
|
|
||||||
align?: "left" | "center" | "right";
|
|
||||||
label?: string;
|
|
||||||
className?: string;
|
|
||||||
prop?: string;
|
|
||||||
width?: number;
|
|
||||||
minWidth?: number | string;
|
|
||||||
renderHeader?: Function;
|
|
||||||
sortable?: boolean | string;
|
|
||||||
sortMethod?: Function;
|
|
||||||
sortBy?: string | Function | unknown[];
|
|
||||||
resizable?: {
|
|
||||||
type: boolean;
|
|
||||||
default: true;
|
|
||||||
};
|
|
||||||
columnKey?: string;
|
|
||||||
headerAlign?: string;
|
|
||||||
showOverflowTooltip?: boolean;
|
|
||||||
fixed?: boolean | string;
|
|
||||||
formatter?: Function;
|
|
||||||
selectable?: Function;
|
|
||||||
reserveSelection?: boolean;
|
|
||||||
filterMethod?: Function;
|
|
||||||
filteredValue?: unknown[];
|
|
||||||
filters?: unknown[];
|
|
||||||
filterPlacement?: string;
|
|
||||||
filterMultiple?: {
|
|
||||||
type: boolean;
|
|
||||||
default: true;
|
|
||||||
};
|
|
||||||
index?: number | Function;
|
|
||||||
sortOrders?: unknown[];
|
|
||||||
}
|
|
||||||
|
|
||||||
export declare interface Table extends TableOptions {
|
|
||||||
props?: {};
|
|
||||||
columns: TableColumn[];
|
|
||||||
}
|
|
7
src/cool/modules/crud/types/upsert.d.ts
vendored
7
src/cool/modules/crud/types/upsert.d.ts
vendored
@ -1,7 +0,0 @@
|
|||||||
import { Form, FormItem, FormRef } from "./form";
|
|
||||||
|
|
||||||
export declare type UpsertItem = FormItem;
|
|
||||||
|
|
||||||
export declare type UpsertRef = FormRef;
|
|
||||||
|
|
||||||
export declare type Upsert = Form;
|
|
@ -1,115 +0,0 @@
|
|||||||
import cloneDeep from "clone-deep";
|
|
||||||
import flat from "array.prototype.flat";
|
|
||||||
import merge from "merge";
|
|
||||||
|
|
||||||
export function isArray(value: any) {
|
|
||||||
if (typeof Array.isArray === "function") {
|
|
||||||
return Array.isArray(value);
|
|
||||||
} else {
|
|
||||||
return Object.prototype.toString.call(value) === "[object Array]";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isObject(value: any) {
|
|
||||||
return Object.prototype.toString.call(value) === "[object Object]";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNumber(value: any) {
|
|
||||||
return !isNaN(Number(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isFunction(value: any) {
|
|
||||||
return typeof value === "function";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isString(value: any) {
|
|
||||||
return typeof value === "string";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isNull(value: any) {
|
|
||||||
return !value && value !== 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isBoolean(value: any) {
|
|
||||||
return typeof value === "boolean";
|
|
||||||
}
|
|
||||||
|
|
||||||
export function isEmpty(value: any) {
|
|
||||||
if (isArray(value)) {
|
|
||||||
return value.length === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isObject(value)) {
|
|
||||||
return Object.keys(value).length === 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value === "" || value === undefined || value === null;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function clone(obj: any) {
|
|
||||||
return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
|
|
||||||
}
|
|
||||||
|
|
||||||
export function dataset(obj: any, key: string, value: any) {
|
|
||||||
const isGet = value === undefined;
|
|
||||||
let d = obj;
|
|
||||||
|
|
||||||
const arr = flat(
|
|
||||||
key.split(".").map((e) => {
|
|
||||||
if (e.includes("[")) {
|
|
||||||
return e.split("[").map((e) => e.replace(/"/g, ""));
|
|
||||||
} else {
|
|
||||||
return e;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
try {
|
|
||||||
for (let i = 0; i < arr.length; i++) {
|
|
||||||
const e: any = arr[i];
|
|
||||||
let n: any = null;
|
|
||||||
|
|
||||||
if (e.includes("]")) {
|
|
||||||
const [k, v] = e.replace("]", "").split(":");
|
|
||||||
|
|
||||||
if (v) {
|
|
||||||
n = d.findIndex((x: any) => x[k] == v);
|
|
||||||
} else {
|
|
||||||
n = Number(n);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
n = e;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (i != arr.length - 1) {
|
|
||||||
d = d[n];
|
|
||||||
} else {
|
|
||||||
if (isGet) {
|
|
||||||
return d[n];
|
|
||||||
} else {
|
|
||||||
d[n] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
} catch (e) {
|
|
||||||
console.error("格式错误", `${key}`);
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function contains(parent: any, node: any) {
|
|
||||||
return parent !== node && parent && parent.contains(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function deepMerge(a: any, b: any) {
|
|
||||||
let k;
|
|
||||||
for (k in b) {
|
|
||||||
a[k] =
|
|
||||||
a[k] && a[k].toString() === "[object Object]" ? deepMerge(a[k], b[k]) : (a[k] = b[k]);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { cloneDeep, flat, merge };
|
|
@ -1,29 +0,0 @@
|
|||||||
import mitt from "mitt";
|
|
||||||
|
|
||||||
const emitter: any = mitt();
|
|
||||||
|
|
||||||
class Emitter {
|
|
||||||
id: number;
|
|
||||||
|
|
||||||
constructor(id?: number) {
|
|
||||||
this.id = id || 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
send(type: string, name: string, ...args: any[]) {
|
|
||||||
emitter[type](`${this.id}__${name}`, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
on(name: string, ...args: any[]) {
|
|
||||||
this.send("on", name, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
emit(name: string, ...args: any[]) {
|
|
||||||
this.send("emit", name, ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
off(name: string, ...args: any[]) {
|
|
||||||
this.send("off", name, ...args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Emitter;
|
|
@ -1,33 +0,0 @@
|
|||||||
import { isString, isBoolean, isFunction } from "./index";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* parse hidden
|
|
||||||
* 1 Boolean
|
|
||||||
* 2 Function({ scope })
|
|
||||||
* 3 :[prop] is bind form[prop] value
|
|
||||||
* @param {*} value
|
|
||||||
*/
|
|
||||||
export default function (method: string, { value, scope, data = {} }: any) {
|
|
||||||
if (data) {
|
|
||||||
data.isAdd = !data.isEdit;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (method === "hidden") {
|
|
||||||
if (isBoolean(value)) {
|
|
||||||
return value;
|
|
||||||
} else if (isString(value)) {
|
|
||||||
const prop = value.substring(1, value.length);
|
|
||||||
|
|
||||||
switch (value[0]) {
|
|
||||||
case "@":
|
|
||||||
return !scope[prop];
|
|
||||||
case ":":
|
|
||||||
return data[prop];
|
|
||||||
}
|
|
||||||
} else if (isFunction(value)) {
|
|
||||||
return value({ scope, ...data });
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,153 +0,0 @@
|
|||||||
// @ts-nocheck
|
|
||||||
import { h, resolveComponent, toRaw } from "vue";
|
|
||||||
import { isFunction, isString, isObject } from "./index";
|
|
||||||
|
|
||||||
const Regs: string[] = [];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 解析节点
|
|
||||||
* @param {*} vnode
|
|
||||||
* @param {{scope,prop,children}} options
|
|
||||||
*/
|
|
||||||
function parseNode(vnode: any, options: any) {
|
|
||||||
const { scope, prop, slots } = options || [];
|
|
||||||
|
|
||||||
// 插槽模式渲染
|
|
||||||
if (vnode.name.indexOf("slot-") == 0) {
|
|
||||||
const rn = slots[vnode.name];
|
|
||||||
|
|
||||||
if (rn) {
|
|
||||||
return rn({ scope });
|
|
||||||
} else {
|
|
||||||
return <cl-error-message title={`组件渲染失败,未找到插槽:${vnode.name}`} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 是否全局注册
|
|
||||||
let isReg: boolean = Regs.includes(vnode.name);
|
|
||||||
|
|
||||||
// 实例模式下,先注册到全局,再分解组件渲染
|
|
||||||
if (vnode.__file && !isReg) {
|
|
||||||
window.__app__.component(vnode.name, vnode);
|
|
||||||
isReg = true;
|
|
||||||
Regs.push(vnode.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组件参数
|
|
||||||
const props = {
|
|
||||||
...vnode.props,
|
|
||||||
...vnode,
|
|
||||||
...vnode.attrs,
|
|
||||||
scope
|
|
||||||
};
|
|
||||||
|
|
||||||
// 删除多余数据
|
|
||||||
delete props._children;
|
|
||||||
|
|
||||||
// 添加双向绑定
|
|
||||||
if (props && scope) {
|
|
||||||
props.modelValue = scope[prop];
|
|
||||||
props["onUpdate:modelValue"] = function (val: any) {
|
|
||||||
scope[prop] = val;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组件实例渲染
|
|
||||||
if (props.render && !isReg) {
|
|
||||||
return h(props, props);
|
|
||||||
}
|
|
||||||
|
|
||||||
return h(toRaw(resolveComponent(vnode.name)), props, {
|
|
||||||
default: () => {
|
|
||||||
return vnode._children;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 渲染节点
|
|
||||||
* @param {*} vnode
|
|
||||||
* @param {*} options
|
|
||||||
*/
|
|
||||||
export function renderNode(vnode: any, { prop, scope, slots }: any) {
|
|
||||||
if (!vnode) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (vnode.__v_isVNode) {
|
|
||||||
return vnode;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组件名渲染
|
|
||||||
if (isString(vnode)) {
|
|
||||||
return parseNode({ name: vnode }, { scope, slots });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 方法回调
|
|
||||||
if (isFunction(vnode)) {
|
|
||||||
return vnode({ scope, h });
|
|
||||||
}
|
|
||||||
|
|
||||||
// jsx 模式
|
|
||||||
if (isObject(vnode)) {
|
|
||||||
if (vnode.name) {
|
|
||||||
// 扩展组件
|
|
||||||
const keys = ["el-select", "el-radio-group", "el-checkbox-group"];
|
|
||||||
|
|
||||||
if (keys.includes(vnode.name)) {
|
|
||||||
// 设置内容
|
|
||||||
vnode._children = (
|
|
||||||
<div>
|
|
||||||
{(vnode.options || []).map((e: any, i: number) => {
|
|
||||||
// 下拉框
|
|
||||||
if (vnode.name == "el-select") {
|
|
||||||
let label: any, value: any;
|
|
||||||
|
|
||||||
if (isString(e)) {
|
|
||||||
label = value = e;
|
|
||||||
} else if (isObject(e)) {
|
|
||||||
label = e.label;
|
|
||||||
value = e.value;
|
|
||||||
} else {
|
|
||||||
return (
|
|
||||||
<cl-error-message
|
|
||||||
title={`组件渲染失败,options 参数错误`}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<el-option key={i} label={label} value={value} {...e.props} />
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// 单选框组
|
|
||||||
else if (vnode.name == "el-radio-group") {
|
|
||||||
return h(<el-radio key={i} label={e.value}></el-radio>, e.props, {
|
|
||||||
default() {
|
|
||||||
return <span>{e.label}</span>;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// 多选框组
|
|
||||||
else if (vnode.name == "el-checkbox-group") {
|
|
||||||
return (
|
|
||||||
<el-checkbox key={i} label={e.value} {...e.props}>
|
|
||||||
{e.label}
|
|
||||||
</el-checkbox>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
|
|
||||||
return parseNode(vnode, { prop, scope });
|
|
||||||
} else {
|
|
||||||
return parseNode(vnode, { prop, scope, slots });
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return <cl-error-message title={`组件渲染失败,组件 name 不能为空`} />;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,7 +6,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { AdvSearchItem } from "/$/crud/types";
|
import { AdvSearchItem } from "cl-admin-crud-vue3/types";
|
||||||
import { defineComponent, ref } from "vue";
|
import { defineComponent, ref } from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { ContextMenu } from "/$/crud";
|
import { ContextMenu } from "cl-admin-crud-vue3";
|
||||||
import { ElMessage } from "element-plus";
|
import { ElMessage } from "element-plus";
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, ref, resolveComponent, h } from "vue";
|
import { defineComponent, ref, resolveComponent, h } from "vue";
|
||||||
import { CrudLoad, FormItem, FormRef } from "/$/crud/types";
|
import { CrudLoad, FormItem, FormRef } from "cl-admin-crud-vue3/types";
|
||||||
import { TestService } from "../../utils/service";
|
import { TestService } from "../../utils/service";
|
||||||
import Test from "./render/test.vue";
|
import Test from "./render/test.vue";
|
||||||
import Test2 from "./render/test2";
|
import Test2 from "./render/test2";
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { QueryList } from "/$/crud/types";
|
import { QueryList } from "cl-admin-crud-vue3/types";
|
||||||
import { defineComponent, ref } from "vue";
|
import { defineComponent, ref } from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent, onMounted, ref } from "vue";
|
import { defineComponent, onMounted, ref } from "vue";
|
||||||
import { TableColumn } from "/$/crud/types";
|
import { TableColumn } from "cl-admin-crud-vue3/types";
|
||||||
import { useRefs } from "/@/core";
|
import { useRefs } from "/@/core";
|
||||||
import Test2 from "./render/test2";
|
import Test2 from "./render/test2";
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { UpsertItem, UpsertRef } from "/$/crud/types";
|
import { UpsertItem, UpsertRef } from "cl-admin-crud-vue3/types";
|
||||||
import { defineComponent, ref } from "vue";
|
import { defineComponent, ref } from "vue";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from "vue";
|
||||||
import { CrudLoad } from "/$/crud/types";
|
import { CrudLoad } from "cl-admin-crud-vue3/types";
|
||||||
import { TestService } from "../utils/service";
|
import { TestService } from "../utils/service";
|
||||||
import Dialog from "../components/crud/dialog.vue";
|
import Dialog from "../components/crud/dialog.vue";
|
||||||
import ContextMenu from "../components/crud/context-menu.vue";
|
import ContextMenu from "../components/crud/context-menu.vue";
|
||||||
|
@ -218,7 +218,7 @@ import { computed, defineComponent, inject, onMounted, reactive } from "vue";
|
|||||||
import { ElMessage, ElMessageBox } from "element-plus";
|
import { ElMessage, ElMessageBox } from "element-plus";
|
||||||
import Draggable from "vuedraggable";
|
import Draggable from "vuedraggable";
|
||||||
import { checkPerm } from "/$/base";
|
import { checkPerm } from "/$/base";
|
||||||
import { ContextMenu } from "/$/crud";
|
import { ContextMenu } from "cl-admin-crud-vue3";
|
||||||
import Cron from "../components/cron";
|
import Cron from "../components/cron";
|
||||||
import { useRefs } from "/@/core";
|
import { useRefs } from "/@/core";
|
||||||
|
|
||||||
|
@ -37,8 +37,8 @@ import { ElMessage, ElMessageBox } from "element-plus";
|
|||||||
import { computed, defineComponent, inject, ref, watch } from "vue";
|
import { computed, defineComponent, inject, ref, watch } from "vue";
|
||||||
import { useStore } from "vuex";
|
import { useStore } from "vuex";
|
||||||
import { isEmpty } from "/@/core/utils";
|
import { isEmpty } from "/@/core/utils";
|
||||||
import { ContextMenu } from "/$/crud";
|
import { ContextMenu } from "cl-admin-crud-vue3";
|
||||||
import { useRefs } from "/$/crud/hooks/core";
|
import { useRefs } from "/@/core";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "cl-upload-space-category",
|
name: "cl-upload-space-category",
|
||||||
|
@ -47,7 +47,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { computed, defineComponent, inject } from "vue";
|
import { computed, defineComponent, inject } from "vue";
|
||||||
import { ContextMenu } from "/$/crud";
|
import { ContextMenu } from "cl-admin-crud-vue3";
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "cl-upload-space-item",
|
name: "cl-upload-space-item",
|
||||||
|
11
src/shims-vue.d.ts
vendored
11
src/shims-vue.d.ts
vendored
@ -5,6 +5,17 @@ declare module "*.vue" {
|
|||||||
export default component;
|
export default component;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
declare module "cl-admin-crud-vue3" {
|
||||||
|
import type { ClContextMenu } from "cl-admin-crud-vue3/types";
|
||||||
|
import type { Plugin } from "vue";
|
||||||
|
|
||||||
|
const ContextMenu: ClContextMenu;
|
||||||
|
const Crud: Plugin;
|
||||||
|
|
||||||
|
export { ContextMenu, Crud };
|
||||||
|
export * from "cl-admin-crud-vue3";
|
||||||
|
}
|
||||||
|
|
||||||
declare module "array.prototype.flat" {
|
declare module "array.prototype.flat" {
|
||||||
function Flat(list: any[]): any[];
|
function Flat(list: any[]): any[];
|
||||||
export default Flat;
|
export default Flat;
|
||||||
|
30
yarn.lock
30
yarn.lock
@ -307,7 +307,7 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad"
|
||||||
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==
|
||||||
|
|
||||||
"@types/lodash@^4.14.168":
|
"@types/lodash@^4.14.161", "@types/lodash@^4.14.168":
|
||||||
version "4.14.168"
|
version "4.14.168"
|
||||||
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
|
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008"
|
||||||
integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
|
integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q==
|
||||||
@ -930,6 +930,18 @@ change-case@^4.1.2:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
fsevents "~2.3.1"
|
fsevents "~2.3.1"
|
||||||
|
|
||||||
|
cl-admin-crud-vue3@^0.1.3:
|
||||||
|
version "0.1.3"
|
||||||
|
resolved "https://registry.nlark.com/cl-admin-crud-vue3/download/cl-admin-crud-vue3-0.1.3.tgz?cache=0&sync_timestamp=1618901222085&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcl-admin-crud-vue3%2Fdownload%2Fcl-admin-crud-vue3-0.1.3.tgz#fc53ce0b42cecd243db6707f862d26a78406dcdb"
|
||||||
|
integrity sha1-/FPOC0LOzSQ9tnB/hi0mp4QG3Ns=
|
||||||
|
dependencies:
|
||||||
|
array.prototype.flat "^1.2.4"
|
||||||
|
core-js "^3.6.5"
|
||||||
|
element-plus "^1.0.2-beta.40"
|
||||||
|
merge "^2.1.1"
|
||||||
|
mitt "^2.1.0"
|
||||||
|
vue "^3.0.0"
|
||||||
|
|
||||||
class-utils@^0.3.5:
|
class-utils@^0.3.5:
|
||||||
version "0.3.6"
|
version "0.3.6"
|
||||||
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
|
resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463"
|
||||||
@ -1306,6 +1318,20 @@ element-plus@1.0.2-beta.35:
|
|||||||
normalize-wheel "^1.0.1"
|
normalize-wheel "^1.0.1"
|
||||||
resize-observer-polyfill "^1.5.1"
|
resize-observer-polyfill "^1.5.1"
|
||||||
|
|
||||||
|
element-plus@^1.0.2-beta.40:
|
||||||
|
version "1.0.2-beta.40"
|
||||||
|
resolved "https://registry.npm.taobao.org/element-plus/download/element-plus-1.0.2-beta.40.tgz#30fc9b161496ae587fab86235c80b728ea43d909"
|
||||||
|
integrity sha1-MPybFhSWrlh/q4YjXIC3KOpD2Qk=
|
||||||
|
dependencies:
|
||||||
|
"@popperjs/core" "^2.4.4"
|
||||||
|
"@types/lodash" "^4.14.161"
|
||||||
|
async-validator "^3.4.0"
|
||||||
|
dayjs "1.x"
|
||||||
|
lodash "^4.17.20"
|
||||||
|
mitt "^2.1.0"
|
||||||
|
normalize-wheel "^1.0.1"
|
||||||
|
resize-observer-polyfill "^1.5.1"
|
||||||
|
|
||||||
emoji-regex@^8.0.0:
|
emoji-regex@^8.0.0:
|
||||||
version "8.0.0"
|
version "8.0.0"
|
||||||
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||||
@ -3760,7 +3786,7 @@ vue-tsc@^0.0.8:
|
|||||||
dependencies:
|
dependencies:
|
||||||
unzipper latest
|
unzipper latest
|
||||||
|
|
||||||
vue@^3.0.11:
|
vue@^3.0.0, vue@^3.0.11:
|
||||||
version "3.0.11"
|
version "3.0.11"
|
||||||
resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.11.tgz#c82f9594cbf4dcc869241d4c8dd3e08d9a8f4b5f"
|
resolved "https://registry.yarnpkg.com/vue/-/vue-3.0.11.tgz#c82f9594cbf4dcc869241d4c8dd3e08d9a8f4b5f"
|
||||||
integrity sha512-3/eUi4InQz8MPzruHYSTQPxtM3LdZ1/S/BvaU021zBnZi0laRUyH6pfuE4wtUeLvI8wmUNwj5wrZFvbHUXL9dw==
|
integrity sha512-3/eUi4InQz8MPzruHYSTQPxtM3LdZ1/S/BvaU021zBnZi0laRUyH6pfuE4wtUeLvI8wmUNwj5wrZFvbHUXL9dw==
|
||||||
|
Loading…
Reference in New Issue
Block a user