发布 7.1.0

This commit is contained in:
神仙都没用 2024-01-25 17:30:25 +08:00
parent fe9c4b2040
commit 897571c7ad
188 changed files with 8820 additions and 2576 deletions

46
build/cool/demo.ts Normal file
View File

@ -0,0 +1,46 @@
import type { Plugin } from "vite";
import { glob } from "glob";
import path from "path";
import { readFileSync } from "fs";
export function demo(enable?: boolean): Plugin {
const virtualModuleIds = ["virtual:demo"];
return {
name: "vite-cool-demo",
enforce: "pre",
resolveId(id) {
if (virtualModuleIds.includes(id)) {
return "\0" + id;
}
},
async load(id) {
if (id === "\0virtual:demo") {
const demo = {};
if (enable) {
const files = await glob("./src/modules/demo/views/crud/components/**", {
stat: true,
withFileTypes: true
});
for (const file of files) {
if (file.isFile()) {
const p = path.join(file.path, file.name);
demo[
p
.replace(/\\/g, "/")
.split("src/modules/demo/views/crud/components/")[1]
] = readFileSync(p, "utf-8");
}
}
}
return `
export const demo = ${JSON.stringify(demo)};
`;
}
}
};
}

View File

@ -1,6 +1,14 @@
import { base } from "./base";
import { virtual } from "./virtual";
import { demo } from "./demo";
export function cool() {
return [base(), virtual()];
return [
// 基础
base(),
// 虚拟模块
virtual(),
// demo 官方示例,代码片段
demo(true)
];
}

View File

@ -42,7 +42,7 @@ function findFiles(dir: string): string[] {
}
export function createSvg(html: string) {
const res = findFiles("./src/modules/");
const res = findFiles("./src/");
return html.replace(
"<body>",

View File

@ -1,6 +1,6 @@
{
"name": "cool-admin",
"version": "7.0.0",
"version": "7.1.0",
"scripts": {
"dev": "vite --host",
"build": "vite build",
@ -9,7 +9,7 @@
"lint:eslint": "eslint \"./src/**/*.{vue,ts,tsx}\" --fix"
},
"dependencies": {
"@cool-vue/crud": "^7.1.3",
"@cool-vue/crud": "^7.1.10",
"@element-plus/icons-vue": "^2.1.0",
"@vueuse/core": "^10.4.0",
"@wangeditor/editor": "^5.1.23",
@ -22,6 +22,7 @@
"element-plus": "^2.4.3",
"file-saver": "^2.0.5",
"lodash-es": "^4.17.21",
"marked": "^11.1.1",
"mitt": "^3.0.1",
"mockjs": "^1.1.0",
"monaco-editor": "0.36.0",
@ -53,6 +54,7 @@
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"eslint-plugin-vue": "^9.17.0",
"glob": "^10.3.10",
"lodash": "^4.17.21",
"magic-string": "^0.30.3",
"prettier": "^3.1.0",

View File

@ -82,39 +82,6 @@ declare type Browser = {
isMini: boolean;
};
// hook
declare namespace Hook {
interface Options {
form: obj;
prop: string;
method: "submit" | "bind";
}
type fn = (value: any, options: Options) => any;
type FormPipe =
| "number"
| "string"
| "split"
| "join"
| "boolean"
| "booleanNumber"
| "datetimeRange"
| "splitJoin"
| "json"
| "empty"
| fn;
type FormPipes = FormPipe | FormPipe[];
type Form =
| string
| {
bind?: FormPipes;
submit?: FormPipes;
};
}
// render
declare namespace Render {
type OpButton =
@ -473,6 +440,26 @@ declare namespace ClForm {
[key: string]: any;
}
type HookFn = (
value: any,
options: { form: obj; prop: string; method: "submit" | "bind" }
) => any;
type HookKey =
| "number"
| "string"
| "split"
| "join"
| "boolean"
| "booleanNumber"
| "datetimeRange"
| "splitJoin"
| "json"
| "empty"
| (string & {});
type HookPipe = HookKey | HookFn;
interface Item<T = any> {
type?: "tabs";
prop?: PropKey<T>;
@ -501,14 +488,19 @@ declare namespace ClForm {
xl: any;
tag: string;
};
hook?: Hook.Form;
group?: string;
collapse?: boolean;
value?: any;
label?: string;
renderLabel?: any;
flex?: boolean;
hidden?: (options: { scope: obj }) => boolean;
hook?:
| HookKey
| {
bind?: HookPipe | HookPipe[];
submit?: HookPipe | HookPipe[];
};
hidden?: boolean | ((options: { scope: obj }) => boolean);
prepend?: Render.Component;
component?: Render.Component;
append?: Render.Component;
@ -632,16 +624,12 @@ declare namespace ClUpsert {
}
}
interface UpsertOptions {
items: ClForm.Items;
}
declare namespace ClAdvSearch {
interface Config<T = any> {
items?: ClForm.Item[];
title?: string;
size?: string | number;
op?: Array<"clear" | "reset" | "close" | "search">;
op?: ("clear" | "reset" | "close" | "search" | `slot-${string}`)[];
onSearch?(data: T, options: { next: ClCrud.Service["api"]["page"]; close(): void }): void;
}
@ -674,9 +662,8 @@ declare namespace ClSearch {
declare namespace ClContextMenu {
interface Item {
label: string;
icon?: string;
prefixIcon?: string;
suffixIcon?: string;
prefixIcon?: any;
suffixIcon?: any;
ellipsis?: boolean;
disabled?: boolean;
hidden?: boolean;

View File

@ -1,6 +1,6 @@
{
"name": "@cool-vue/crud",
"version": "7.1.3",
"version": "7.1.10",
"private": false,
"main": "./dist/index.umd.min.js",
"typings": "types/index.d.ts",

View File

@ -1,16 +1,36 @@
<template>
<div>
<div>CRUD DEMO v7.0.0</div>
<div class="title">CRUD DEMO v7.0.0</div>
<cl-crud ref="Crud">
<cl-row>
<cl-add-btn />
<cl-adv-btn />
</cl-row>
<cl-row>
<cl-table ref="Table" :auto-height="false"></cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<cl-crud>
<cl-add-btn />
<cl-upsert ref="Upsert"></cl-upsert>
<cl-adv-search ref="AdvSearch"></cl-adv-search>
</cl-crud>
<cl-dialog v-model="visible" height="20vh">
<div style="height: 50px" v-for="d in 100" :key="d">{{ d }}</div>
</cl-dialog>
</div>
</template>
<script setup lang="tsx">
import { useTable, useForm, useSearch, useUpsert, useAdvSearch } from "./hooks";
import { useTable, useForm, useUpsert, useCrud } from "./hooks";
import { EditPen } from "@element-plus/icons-vue";
import { ref } from "vue";
interface Data {
name?: string;
@ -18,39 +38,119 @@ interface Data {
[key: string]: any;
}
const visible = ref(true);
const Upsert = useUpsert<Data>({
items: [
{
label: "xx",
type: "tabs",
props: {
labels: [
{
label: "A",
value: "A",
icon: EditPen
},
{
label: "B",
value: "B"
}
]
}
},
{
group: "B",
prop: "age",
component: {
name: "el-input"
}
},
{
group: "B",
prop: "证书",
component: {
name: "el-input"
},
hidden({ scope }) {
return scope.age < 18;
}
},
() => {
return {
group: "A",
hidden: Upsert.value?.mode == "add",
label: "x"
hook: {
bind(value, { form }) {
return "";
},
submit(value, { form }) {}
}
};
}
],
onOpened(data) {}
onOpened(data) {
Upsert.value?.setForm("age", "18");
}
});
const Table = useTable<Data>({
contextMenu: [
{
label: "带图标",
prefixIcon: EditPen
},
{
label: "多层级",
children: [
{
label: "A",
children: [
{
label: "A-1"
}
]
},
{
label: "B"
}
]
}
],
columns: [
{
label: "xx",
prop: "a",
label: "姓名",
prop: "name",
search: {
component: {
name: "el-date-picker"
}
}
},
{
label: "手机号",
prop: "phone",
search: {
component: {
name: "el-date-picker"
}
}
},
{
type: "op"
}
]
});
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
const Form = useForm<Data>();
Form.value?.open({
@ -61,22 +161,12 @@ Form.value?.open({
}
]
});
const Search = useSearch<Data>({
items: [
{
label: "xx",
prop: "age"
}
]
});
const AdvSearch = useAdvSearch<Data>({
items: [
{
label: "xx",
prop: "age"
}
]
});
</script>
<style scoped>
.title {
text-align: center;
font-size: 14px;
font-weight: bold;
}
</style>

View File

@ -78,6 +78,7 @@ export default defineComponent({
function reset() {
Form.value?.reset();
emit("reset");
search();
}
// 清空数据
@ -160,9 +161,9 @@ export default defineComponent({
v-model={visible.value}
direction="rtl"
with-header={false}
size={browser.isMini ? "100%" : props.size}>
size={browser.isMini ? "100%" : config.size}>
<div class="cl-adv-search__header">
<span class="text">{props.title || crud.dict.label.advSearch}</span>
<span class="text">{config.title || crud.dict.label.advSearch}</span>
<el-icon size={20} onClick={close}>
<Close />
</el-icon>

View File

@ -1,7 +1,9 @@
import { defineComponent, nextTick, onMounted, reactive, ref, h, render } from "vue";
import { defineComponent, nextTick, onMounted, reactive, ref, h, render, toRaw } from "vue";
import { isString } from "lodash-es";
import { addClass, contains, removeClass } from "../../utils";
import { useRefs } from "../../hooks";
import { ElIcon } from "element-plus";
import { ArrowRight } from "@element-plus/icons-vue";
const ClContextMenu = defineComponent({
name: "cl-context-menu",
@ -89,7 +91,7 @@ const ClContextMenu = defineComponent({
// 点击样式
if (options.hover) {
let d = options.hover === true ? {} : options.hover;
const d = options.hover === true ? {} : options.hover;
targetEl = event.target;
if (targetEl && isString(targetEl.className)) {
@ -187,15 +189,22 @@ const ClContextMenu = defineComponent({
.map((e, i) => {
const id = `${pId}-${i}`;
if (!e.suffixIcon) {
// 默认图标
if (e.children) {
e.suffixIcon = ArrowRight;
}
}
return (
<div
class={{
"is-active": ids.value.includes(id),
"is-ellipsis": e.ellipsis,
"is-ellipsis": e.ellipsis ?? true,
"is-disabled": e.disabled
}}>
{/* 前缀图标 */}
{e.prefixIcon && <i class={e.prefixIcon}></i>}
{e.prefixIcon && <ElIcon>{h(toRaw(e.prefixIcon))}</ElIcon>}
{/* 标题 */}
<span
@ -206,9 +215,9 @@ const ClContextMenu = defineComponent({
</span>
{/* 后缀图标 */}
{e.suffixIcon && <i class={e.suffixIcon}></i>}
{e.suffixIcon && <ElIcon>{h(toRaw(e.suffixIcon))}</ElIcon>}
{/* 子集*/}
{/* 子集 */}
{e.children &&
e.showChildren &&
deep(e.children, id, level + 1)}

View File

@ -50,7 +50,12 @@ export default defineComponent({
// 隐藏头部元素
hideHeader: Boolean,
// 关闭前
beforeClose: Function
beforeClose: Function,
// 是否需要滚动条
scrollbar: {
type: Boolean,
default: true
}
},
emits: ["update:modelValue", "fullscreen-change"],
@ -242,16 +247,28 @@ export default defineComponent({
return renderHeader();
},
default() {
return (
<el-scrollbar
class="cl-dialog__container"
key={cacheKey.value}
style={{ height: props.height }}>
<div class="cl-dialog__default" style={{ padding: props.padding }}>
const height = isFullscreen.value ? "100%" : props.height;
const style = {
padding: props.padding,
height
};
function content() {
return (
<div class="cl-dialog__default" style={style} key={cacheKey.value}>
{slots.default?.()}
</div>
</el-scrollbar>
);
);
}
if (props.scrollbar) {
style.height = "auto";
return <el-scrollbar height={height}>{content()}</el-scrollbar>;
} else {
return content();
}
},
footer() {
const d = slots.footer?.();

View File

@ -119,21 +119,32 @@ export default defineComponent({
// 拷贝表单值
const d = cloneDeep(form);
// 过滤隐藏的表单项
config.items.forEach((e) => {
if (e._hidden) {
function deep(e: ClForm.Item) {
if (e.prop) {
delete d[e.prop];
// 过滤隐藏的表单项
if (e._hidden) {
if (e.prop) {
delete d[e.prop];
}
}
// hook 提交处理
if (e.hook) {
formHook.submit({
...e,
value: e.prop ? d[e.prop] : undefined,
form: d
});
}
}
if (e.children) {
e.children.forEach(deep);
}
}
if (e.hook) {
formHook.submit({
...e,
value: e.prop ? d[e.prop] : undefined,
form: d
});
}
deep(e);
});
// 处理 "-" 多层级
@ -152,7 +163,7 @@ export default defineComponent({
let f: any = d[a];
// 设置默认值
arr.forEach((e: any) => {
arr.forEach((e) => {
if (!f[e]) {
f[e] = {};
}
@ -249,7 +260,7 @@ export default defineComponent({
}
// 设置表单数据
config.items.map((e) => {
config.items.forEach((e) => {
function deep(e: ClForm.Item) {
if (e.prop) {
// 解析 prop
@ -260,7 +271,7 @@ export default defineComponent({
// prop 合并
Tabs.mergeProp(e);
// 绑定值
// hook 绑定值
formHook.bind({
...e,
value: form[e.prop] !== undefined ? form[e.prop] : cloneDeep(e.value),
@ -274,17 +285,17 @@ export default defineComponent({
message: `${e.label}${dict.label.nonEmpty}`
};
}
// 子集
if (e.children) {
e.children.forEach(deep);
}
}
// 设置 tabs 默认值
if (e.type == "tabs") {
Tabs.set(e.value);
}
// 子集
if (e.children) {
e.children.forEach(deep);
}
}
deep(e);
@ -312,11 +323,19 @@ export default defineComponent({
// 绑定表单数据
function bindForm(data: any) {
config.items.forEach((e) => {
formHook.bind({
...e,
value: e.prop ? data[e.prop] : undefined,
form: data
});
function deep(e: ClForm.Item) {
formHook.bind({
...e,
value: e.prop ? data[e.prop] : undefined,
form: data
});
if (e.children) {
e.children.forEach(deep);
}
}
deep(e);
});
Object.assign(form, data);
@ -344,105 +363,104 @@ export default defineComponent({
const isLoaded = e.component && Tabs.isLoaded(e.group);
// 表单项
const FormItem = isLoaded
? h(
<el-form-item
class={{
"no-label": !(e.renderLabel || e.label),
"has-children": !!e.children
}}
data-group={e.group}
data-prop={e.prop}
label-width={props.inline ? "auto" : ""}
label={e.label}
prop={e.prop}
rules={isDisabled ? null : e.rules}
required={e._hidden ? false : e.required}
v-show={inGroup && !e._hidden}
/>,
e.props,
{
label() {
return e.renderLabel
? renderNode(e.renderLabel, {
scope: form,
render: "slot",
slots
})
: e.label;
},
default() {
return (
<div>
<div class="cl-form-item">
{["prepend", "component", "append"]
.filter((k) => e[k])
.map((name) => {
const children = e.children && (
<div class="cl-form-item__children">
<el-row gutter={10}>
{e.children.map(renderFormItem)}
</el-row>
</div>
);
const FormItem = h(
<el-form-item
class={{
"no-label": !(e.renderLabel || e.label),
"has-children": !!e.children
}}
key={e.prop}
data-group={e.group || "-"}
data-prop={e.prop || "-"}
label-width={props.inline ? "auto" : ""}
label={e.label}
prop={e.prop}
rules={isDisabled ? null : e.rules}
required={e._hidden ? false : e.required}
v-show={inGroup && !e._hidden}
/>,
e.props,
{
label() {
return e.renderLabel
? renderNode(e.renderLabel, {
scope: form,
render: "slot",
slots
})
: e.label;
},
default() {
return (
<div>
<div class="cl-form-item">
{["prepend", "component", "append"]
.filter((k) => e[k])
.map((name) => {
const children = e.children && (
<div class="cl-form-item__children">
<el-row gutter={10}>
{e.children.map(renderFormItem)}
</el-row>
</div>
);
const Item = renderNode(e[name], {
item: e,
prop: e.prop,
scope: form,
slots,
children,
_data: {
isDisabled
const Item = renderNode(e[name], {
item: e,
prop: e.prop,
scope: form,
slots,
children,
_data: {
isDisabled
}
});
return (
<div
v-show={!e.collapse}
class={[
`cl-form-item__${name}`,
{
flex1: e.flex !== false
}
});
]}
style={e[name].style}>
{Item}
</div>
);
})}
</div>
return (
<div
v-show={!e.collapse}
class={[
`cl-form-item__${name}`,
{
flex1: e.flex !== false
}
]}
style={e[name].style}>
{Item}
</div>
);
})}
</div>
{isBoolean(e.collapse) && (
<div
class="cl-form-item__collapse"
onClick={() => {
Action.collapseItem(e);
}}>
<el-divider content-position="center">
{e.collapse
? dict.label.seeMore
: dict.label.hideContent}
</el-divider>
</div>
)}
{isBoolean(e.collapse) && (
<div
class="cl-form-item__collapse"
onClick={() => {
Action.collapseItem(e);
}}>
<el-divider content-position="center">
{e.collapse
? dict.label.seeMore
: dict.label.hideContent}
</el-divider>
</div>
);
}
}
)
: null;
)}
</div>
);
}
}
);
// 行内
if (props.inline) {
return FormItem;
}
return (
<el-col key={e.prop} span={e.span || style.form.span} {...e.col}>
// 是否行内
const Item = props.inline ? (
FormItem
) : (
<el-col span={e.span || style.form.span} {...e.col} v-show={inGroup && !e._hidden}>
{FormItem}
</el-col>
);
return isLoaded ? Item : null;
}
// 渲染表单

View File

@ -87,8 +87,20 @@ export default defineComponent({
// 重置
function reset() {
const d: any = {};
config.items?.map((e) => {
d[e.prop!] = undefined;
});
// 重置表单
Form.value?.reset();
emit("reset");
// 列表刷新
crud.refresh(d);
// 重置事件
emit("reset", d);
}
expose({
@ -123,6 +135,7 @@ export default defineComponent({
append() {
return (
<el-form-item>
{/* 搜索按钮 */}
<el-button
type="primary"
loading={loading.value}
@ -132,11 +145,16 @@ export default defineComponent({
}}>
{crud.dict.label.search}
</el-button>
{/* 重置按钮 */}
{config.resetBtn && (
<el-button size={style.size} onClick={reset}>
{crud.dict.label.reset}
</el-button>
)}
{/* 自定义按钮 */}
{slots?.buttons?.(Form.value?.form)}
</el-form-item>
);
},

View File

@ -371,6 +371,7 @@
ul {
display: inline-flex;
white-space: nowrap;
margin: 0;
li {
display: inline-flex;
@ -397,7 +398,9 @@
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);
transition:
transform 0.3s ease-in-out,
width 0.2s 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
background-color: var(--el-color-primary);
}
@ -514,14 +517,7 @@
user-select: none;
}
&__container {
& > .el-scrollbar__wrap > .el-scrollbar__view {
height: 100%;
}
}
&__default {
height: 100%;
box-sizing: border-box;
}

View File

@ -1,6 +1,6 @@
import { isArray, isFunction, isObject, isString } from "lodash-es";
import { isArray, isEmpty, isFunction, isObject, isString } from "lodash-es";
export const format: { [key: string]: Hook.fn } = {
export const format: { [key: string]: ClForm.HookFn } = {
number(value) {
return value ? (isArray(value) ? value.map(Number) : Number(value)) : value;
},
@ -63,6 +63,10 @@ export const format: { [key: string]: Hook.fn } = {
return value === "" ? undefined : value;
}
if (isArray(value)) {
return isEmpty(value) ? undefined : value;
}
return value;
}
};
@ -139,7 +143,7 @@ const formHook = {
}
};
export function registerFormHook(name: string, fn: Hook.fn) {
export function registerFormHook(name: string, fn: ClForm.HookFn) {
format[name] = fn;
}

View File

@ -25,6 +25,10 @@ declare const _default: import("vue").DefineComponent<{
};
hideHeader: BooleanConstructor;
beforeClose: FunctionConstructor;
scrollbar: {
type: BooleanConstructor;
default: boolean;
};
}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
[key: string]: any;
}>, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("update:modelValue" | "fullscreen-change")[], "update:modelValue" | "fullscreen-change", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
@ -54,6 +58,10 @@ declare const _default: import("vue").DefineComponent<{
};
hideHeader: BooleanConstructor;
beforeClose: FunctionConstructor;
scrollbar: {
type: BooleanConstructor;
default: boolean;
};
}>> & {
"onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
"onFullscreen-change"?: ((...args: any[]) => any) | undefined;
@ -66,5 +74,6 @@ declare const _default: import("vue").DefineComponent<{
controls: unknown[];
fullscreen: boolean;
modelValue: boolean;
scrollbar: boolean;
}, {}>;
export default _default;

View File

@ -56,17 +56,17 @@ export declare function useForm(): {
xl: any;
tag: string;
} | undefined;
hook?: string | {
bind?: Hook.FormPipe | Hook.FormPipe[] | undefined;
submit?: Hook.FormPipe | Hook.FormPipe[] | undefined;
} | undefined;
group?: string | undefined;
collapse?: boolean | undefined;
value?: any;
label?: string | undefined;
renderLabel?: any;
flex?: boolean | undefined;
hidden?: ((options: {
hook?: ClForm.HookKey | {
bind?: ClForm.HookPipe | ClForm.HookPipe[] | undefined;
submit?: ClForm.HookPipe | ClForm.HookPipe[] | undefined;
} | undefined;
hidden?: boolean | ((options: {
scope: obj;
}) => boolean) | undefined;
prepend?: {

View File

@ -143,9 +143,8 @@ export declare function useTable(props: any): {
contextMenu: ("info" | "update" | "delete" | "edit" | "refresh" | {
[x: string]: any;
label: string;
icon?: string | undefined;
prefixIcon?: string | undefined;
suffixIcon?: string | undefined;
prefixIcon?: any;
suffixIcon?: any;
ellipsis?: boolean | undefined;
disabled?: boolean | undefined;
hidden?: boolean | undefined;

View File

@ -1,10 +1,10 @@
/// <reference types="../index" />
export declare const format: {
[key: string]: Hook.fn;
[key: string]: ClForm.HookFn;
};
declare const formHook: {
bind(data: any): void;
submit(data: any): void;
};
export declare function registerFormHook(name: string, fn: Hook.fn): void;
export declare function registerFormHook(name: string, fn: ClForm.HookFn): void;
export default formHook;

View File

@ -1,6 +1,8 @@
export const proxy = {
"/dev/": {
target: "http://127.0.0.1:8001",
target: "http://192.168.0.119:8001",
// target: "http://127.0.0.1:8004",
// target: "https://test-admin.cool-js.cloud",
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/dev/, "")
},

View File

@ -1,13 +1,16 @@
import { App } from "vue";
import { isFunction, orderBy } from "lodash-es";
import { isFunction, orderBy, chain } from "lodash-es";
import { filename } from "../utils";
import { module } from "../module";
import { hmr } from "../hook";
// 扫描文件
const files: any = import.meta.glob("/src/modules/*/{config.ts,service/**,directives/**}", {
eager: true
});
const files: any = import.meta.glob(
"/src/{modules,plugins}/*/{config.ts,service/**,directives/**}",
{
eager: true
}
);
// 模块列表
module.list = hmr.getData("modules", []);
@ -15,7 +18,7 @@ module.list = hmr.getData("modules", []);
// 解析
for (const i in files) {
// 分割
const [, , , name, action] = i.split("/");
const [, , type, name, action] = i.split("/");
// 文件名
const fname = filename(i);
@ -29,6 +32,7 @@ for (const i in files) {
// 数据
const d = m || {
name,
type,
value: null,
services: [],
directives: []

View File

@ -13,6 +13,9 @@ const module = {
get(name: string): Module {
return this.list.find((e) => e.name == name)!;
},
config(name: string) {
return this.get(name).options || {};
},
add(data: Module) {
this.list.push(data);
},

View File

@ -6,6 +6,9 @@ import { useBase } from "/$/base";
import { Loading } from "../utils";
import { config } from "/@/config";
// 基本路径
const baseUrl = import.meta.env.BASE_URL;
// 扫描文件
const files = import.meta.glob(["/src/modules/*/{views,pages}/**/*", "!**/components"]);
@ -26,7 +29,10 @@ const routes: RouteRecordRaw[] = [
// 创建路由器
const router = createRouter({
history: config.app.router.mode == "history" ? createWebHistory() : createWebHashHistory(),
history:
config.app.router.mode == "history"
? createWebHistory(baseUrl)
: createWebHashHistory(baseUrl),
routes
}) as Router;

View File

@ -1,10 +0,0 @@
.cl-avatar {
.el-avatar {
display: block;
margin: 0 auto;
}
img {
width: 100%;
}
}

View File

@ -1,5 +1,5 @@
import { defineComponent, PropType } from "vue";
import "./index.scss";
import { defineComponent, type PropType } from "vue";
import { UserFilled } from "@element-plus/icons-vue";
export default defineComponent({
name: "cl-avatar",
@ -7,7 +7,10 @@ export default defineComponent({
props: {
modelValue: String,
src: String,
icon: null,
icon: {
type: null,
default: UserFilled
},
size: [String, Number] as PropType<"large" | "default" | "small" | number>,
shape: String as PropType<"circle" | "square">,
fit: {
@ -19,18 +22,16 @@ export default defineComponent({
setup(props) {
return () => {
return (
<div class="cl-avatar">
<el-avatar
style={{
height: props.size + "px",
width: props.size + "px"
}}
{...{
...props,
src: props.modelValue || props.src
}}
/>
</div>
<el-avatar
style={{
height: props.size + "px",
width: props.size + "px"
}}
{...{
...props,
src: props.modelValue || props.src
}}
/>
);
};
}

View File

@ -71,13 +71,13 @@ function onCheckChange(_: any, { checkedKeys }: any) {
//
watch(keyword, (val: string) => {
Tree.value.filter(val);
Tree.value?.filter(val);
});
useUpsert({
async onOpened() {
await refresh();
Tree.value.setCheckedKeys(props.modelValue || []);
Tree.value?.setCheckedKeys(props.modelValue || []);
}
});
</script>

View File

@ -18,7 +18,7 @@
preview-teleported
>
<template #error>
<div class="image-slot">
<div class="cl-image__slot">
<el-icon :size="20"><picture-filled /></el-icon>
</div>
</template>
@ -27,7 +27,7 @@
</template>
<script lang="ts">
import { PropType, computed, defineComponent } from "vue";
import { type PropType, computed, defineComponent } from "vue";
import { isArray, isNumber, isString } from "lodash-es";
import { PictureFilled } from "@element-plus/icons-vue";
import { parsePx } from "/@/cool/utils";
@ -91,20 +91,13 @@ export default defineComponent({
<style lang="scss" scoped>
.cl-image {
display: flex;
align-items: center;
.el-image {
display: block;
.image-slot {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background-color: #f7f7f7;
border-radius: 4px;
}
&__slot {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
background-color: #f7f7f7;
border-radius: 4px;
}
}
</style>

View File

@ -73,7 +73,7 @@ watch(keyword, (val: string) => {
useUpsert({
async onOpened() {
await refresh();
Tree.value.setCheckedKeys(
Tree.value?.setCheckedKeys(
(props.modelValue || []).filter((e) => Tree.value.getNode(e)?.isLeaf)
);
}

View File

@ -1,8 +1,8 @@
import { TreeData } from "element-plus/es/components/tree/src/tree.type";
import { ClViewGroup, useViewGroup } from "/$/base";
import { service } from "/@/cool";
import Node from "element-plus/es/components/tree/src/model/node";
import ClAvatar from "../components/avatar/index";
import { type ClViewGroup, useViewGroup } from "/@/plugins/view";
export function useDeptViewGroup(options: DeepPartial<ClViewGroup.Options>) {
const { ViewGroup } = useViewGroup({

View File

@ -1,2 +1 @@
export * from "../components/view/group/hook";
export * from "./dept";

View File

@ -4,7 +4,7 @@
<el-form label-width="100px" :model="form" :disabled="loading">
<el-form-item label="头像">
<cl-upload is-space v-model="form.headImg" />
<cl-upload v-model="form.headImg" />
</el-form-item>
<el-form-item label="昵称">

View File

@ -8,7 +8,7 @@
import { useForm } from "@cool-vue/crud";
import { ElMessage } from "element-plus";
import { isEmpty } from "lodash-es";
import { PropType } from "vue";
import { type PropType } from "vue";
import { useCool } from "/@/cool";
import dayjs from "dayjs";
import { Download } from "@element-plus/icons-vue";
@ -30,6 +30,9 @@ function open() {
props: {
labelPosition: "top"
},
op: {
saveButtonText: "导出"
},
items: [
{
label: "选择菜单",

View File

@ -105,8 +105,8 @@ import { deepTree } from "/@/cool/utils";
import { useStore } from "/$/base/store";
import MenuImp from "./components/imp.vue";
import MenuExp from "./components/exp.vue";
import AutoMenu from "/$/magic/components/auto-menu/index.vue";
import AutoPerms from "/$/magic/components/auto-perms/index.vue";
import AutoMenu from "/$/helper/components/auto-menu/index.vue";
import AutoPerms from "/$/helper/components/auto-perms/index.vue";
const { service, mitt } = useCool();
const { menu } = useStore();
@ -401,5 +401,5 @@ function addPermission({ id }: any) {
});
}
mitt.on("magic.createMenu", refresh);
mitt.on("helper.createMenu", refresh);
</script>

View File

@ -19,19 +19,29 @@
<cl-upsert ref="Upsert">
<template #slot-relevance="{ scope }">
<el-switch
v-model="scope.relevance"
:active-value="1"
:inactive-value="0"
@change="onRelevanceChange"
/>
<span
:style="{
marginLeft: '10px',
fontSize: '12px'
}"
>是否关联上下级</span
>
<div>
<el-row>
<el-switch
v-model="scope.relevance"
:active-value="1"
:inactive-value="0"
/>
<span
:style="{
marginLeft: '10px',
fontSize: '12px'
}"
>
是否关联上下级
</span>
</el-row>
<cl-dept-check
v-model="scope.departmentIdList"
:check-strictly="scope.relevance == 0"
/>
</div>
</template>
</cl-upsert>
</cl-crud>
@ -96,30 +106,20 @@ const Upsert = useUpsert({
{
label: "数据权限",
prop: "relevance",
flex: false,
component: {
name: "slot-relevance"
}
},
{
label: "",
prop: "departmentIdList",
value: [],
component: {
name: "cl-dept-check",
props: {},
style: {
marginTop: "-10px"
}
}
}
],
plugins: [setFocus()],
onSubmit(data, { next }) {
next({
...data,
departmentIdList: data.departmentIdList || []
});
},
onOpened(data) {
onRelevanceChange(data.relevance || 0);
}
plugins: [setFocus()]
});
// cl-table
@ -164,11 +164,4 @@ const Table = useTable({
}
]
});
//
function onRelevanceChange(val: number | string | boolean) {
Upsert.value?.setProps("departmentIdList", {
checkStrictly: val == 0
});
}
</script>

View File

@ -81,7 +81,8 @@ import { deepTree, revDeepTree } from "/@/cool/utils";
import { isArray } from "lodash-es";
import { ContextMenu, setFocus, useForm } from "@cool-vue/crud";
import { Refresh as RefreshIcon, Operation, MoreFilled } from "@element-plus/icons-vue";
import { checkPerm, useViewGroup } from "/$/base";
import { checkPerm } from "/$/base";
import { useViewGroup } from "/@/plugins/view";
const props = defineProps({
drag: {
@ -97,9 +98,8 @@ const props = defineProps({
const emit = defineEmits(["refresh", "user-add"]);
const { service, browser } = useCool();
const { ViewGroup } = useViewGroup();
const Form = useForm();
const { ViewGroup } = useViewGroup();
//
const list = ref<Eps.BaseSysDepartmentEntity[]>([]);

View File

@ -59,9 +59,9 @@
<script lang="ts" name="sys-user" setup>
import { useTable, useUpsert, useCrud } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { useViewGroup } from "../../hooks";
import DeptList from "./components/dept-list.vue";
import UserMove from "./components/user-move.vue";
import { useViewGroup } from "/@/plugins/view";
const { service, refs, setRefs } = useCool();

View File

@ -1,6 +1,6 @@
<template>
<div class="cl-chat__wrap">
<el-badge :value="unCount">
<el-badge :value="unCount" :hidden="!unCount">
<div class="cl-chat__icon" @click="open">
<cl-svg name="icon-notice" :size="16" />
</div>
@ -14,6 +14,7 @@
width="1200px"
padding="0"
keep-alive
:scrollbar="false"
:close-on-click-modal="false"
close-on-press-escape
:controls="['slot-expand', 'cl-flex1', 'fullscreen', 'close']"

View File

@ -1,6 +1,6 @@
import { ModuleConfig } from "/@/cool";
import { addDeclare } from "/$/extend";
import { CodeDeclare } from "./dict";
import { addDeclare } from "/@/plugins/editor-monaco";
export default (): ModuleConfig => {
return {

View File

@ -54,9 +54,9 @@
import { useForm } from "@cool-vue/crud";
import { ElMessage, ElMessageBox } from "element-plus";
import { reactive, ref } from "vue";
import { useViewGroup } from "/$/base";
import { useCool } from "/@/cool";
import { CodeSnippets, Status } from "../dict";
import { useViewGroup } from "/@/plugins/view";
const { service } = useCool();

View File

@ -1,7 +1,7 @@
<template>
<div class="scope">
<div class="h">
<span>cl-context-menu</span>
<el-tag size="small" effect="dark">cl-context-menu</el-tag>
右键菜单
</div>
<div class="c">
@ -17,7 +17,7 @@
import { ContextMenu } from "@cool-vue/crud";
import { ElMessage } from "element-plus";
function open(e: any) {
function open(e: MouseEvent) {
ContextMenu.open(e, {
list: [
{

View File

@ -1,7 +1,7 @@
<template>
<div class="scope">
<div class="h">
<span>v-copy</span>
<el-tag size="small" effect="dark">v-copy</el-tag>
复制到剪贴板
</div>

View File

@ -1,7 +1,7 @@
<template>
<div class="scope">
<div class="h">
<span>cl-crud</span>
<el-tag size="small" effect="dark">cl-crud</el-tag>
增删改查超快的
</div>
<div class="c">

View File

@ -1,7 +1,7 @@
<template>
<div class="scope">
<div class="h">
<span>design-page</span>
<el-tag size="small" effect="dark">design-page</el-tag>
页面设计
</div>
<div class="c">

View File

@ -1,7 +1,7 @@
<template>
<div class="scope">
<div class="h">
<span>cl-editor</span>
<el-tag size="small" effect="dark">cl-editor</el-tag>
编辑器
</div>
<div class="c">

View File

@ -1,7 +1,7 @@
<template>
<div class="scope">
<div class="h">
<span>file</span>
<el-tag size="small" effect="dark">file</el-tag>
文件管理
</div>
<div class="c">

View File

@ -1,18 +1,14 @@
<template>
<div class="scope">
<div class="h">
<span>cl-form</span>
<el-tag size="small" effect="dark">cl-form</el-tag>
很强的表单
</div>
<div class="c">
<form-btn />
<router-link to="/demo/crud?key=cl-form">传送门</router-link>
</div>
<div class="f">
<span class="date">2019/01/01</span>
</div>
</div>
</template>
<script lang="ts" setup>
import FormBtn from "./form-btn.vue";
</script>

View File

@ -1,7 +1,7 @@
<template>
<div class="scope">
<div class="h">
<span>cl-svg</span>
<el-tag size="small" effect="dark">cl-svg</el-tag>
svg图片库
</div>
<div class="c _svg">

View File

@ -1,7 +1,7 @@
<template>
<div class="scope">
<div class="h">
<span>cl-upload</span>
<el-tag size="small" effect="dark">cl-upload</el-tag>
图片上传
</div>
<div class="c">

View File

@ -0,0 +1,7 @@
import { ModuleConfig } from "/@/cool";
export default (): ModuleConfig => {
return {
components: [() => import("./views/crud/components/code.vue")]
};
};

View File

@ -0,0 +1,138 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">base</el-tag>
<span>起步</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['adv-search/base.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="起步" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!--很重要高级搜索组件按钮 -->
<cl-adv-btn />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!--很重要高级搜索组件 -->
<cl-adv-search ref="AdvSearch" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useAdvSearch, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
// cl-adv-search
// cl-form
const AdvSearch = useAdvSearch({
// cl-form
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input",
props: {
clearable: true
}
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input",
props: {
clearable: true
}
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
function refresh(params?: any) {
Crud.value?.refresh(params);
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,152 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">custom</el-tag>
<span>自定义</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['adv-search/custom.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="自定义" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!--很重要高级搜索组件按钮 -->
<cl-adv-btn>更多搜索</cl-adv-btn>
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!--很重要高级搜索组件 -->
<cl-adv-search ref="AdvSearch">
<!-- 自定义按钮 -->
<template #slot-btn>
<el-button @click="toSearch">自定义</el-button>
</template>
</cl-adv-search>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useAdvSearch, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
// cl-adv-search
// cl-form
const AdvSearch = useAdvSearch({
// cl-form
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input",
props: {
clearable: true
}
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input",
props: {
clearable: true
}
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
],
title: "更多搜索",
size: "50%",
op: ["close", "search", "slot-btn"]
});
function refresh(params?: any) {
Crud.value?.refresh(params);
}
//
function toSearch() {
refresh({ page: 1 });
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,41 @@
<template>
<cl-editor-preview name="monaco" :ref="setRefs('preview')" :tabs="tabs" v-if="!isHide">
<el-button @click="open">代码</el-button>
</cl-editor-preview>
</template>
<script setup lang="ts" name="demo-code">
import { useCool } from "/@/cool";
import { type PropType, computed } from "vue";
import { demo } from "virtual:demo";
import { basename } from "/@/cool/utils";
import { isEmpty } from "lodash-es";
const props = defineProps({
files: {
type: Array as PropType<string[]>,
default: () => []
}
});
const { refs, setRefs } = useCool();
//
const isHide = computed(() => isEmpty(demo));
//
const tabs = computed(() => {
return props.files?.map((e) => {
return {
name: basename(e),
language: e.includes(".vue") ? "html" : "typescript",
data: demo[e]
};
});
});
//
function open() {
refs.preview.open();
}
</script>

View File

@ -1,103 +1,128 @@
<template>
<cl-crud ref="Crud">
<cl-row>
<!-- 刷新按钮 -->
<cl-refresh-btn />
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">all</el-tag>
<span>完整示例</span>
</div>
<!-- 新增按钮 -->
<cl-add-btn />
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['crud/all.vue']" />
<!-- 批量删除按钮 -->
<cl-multi-delete-btn />
<cl-dialog v-model="visible" title="完整示例" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!-- 刷新按钮 -->
<cl-refresh-btn />
<!-- 自定义表单 -->
<form-btn />
<!-- 新增按钮 -->
<cl-add-btn />
<!-- 筛选 -->
<cl-filter label="状态筛选">
<!-- 配置props选择后会自动过滤列表 -->
<cl-select :options="options.status" prop="status" :width="120" />
</cl-filter>
<!-- 批量删除按钮 -->
<cl-multi-delete-btn />
<!-- 字典 -->
<cl-filter label="工作(字典)">
<cl-select :options="dict.get('occupation')" prop="occupation" :width="120" />
</cl-filter>
<!-- 筛选 -->
<cl-filter label="状态筛选">
<!-- 配置props选择后会自动过滤列表 -->
<cl-select :options="options.status" prop="status" :width="120" />
</cl-filter>
<cl-flex1 />
<!-- 字典 -->
<cl-filter label="工作(字典)">
<cl-select
:options="dict.get('occupation')"
prop="occupation"
:width="120"
/>
</cl-filter>
<!-- 导入 -->
<cl-import-btn template="/用户导入模版.xlsx" />
<cl-flex1 />
<!-- 导出 -->
<cl-export-btn :columns="Table?.columns" />
<!-- 导入 -->
<cl-import-btn template="/用户导入模版.xlsx" />
<!-- 自定义列 -->
<cl-column-custom :columns="Table?.columns" :ref="setRefs('columnCustom')" />
<!-- 导出 -->
<cl-export-btn :columns="Table?.columns" />
<!-- 关键字搜索 -->
<cl-search-key placeholder="搜索姓名、手机号" :width="250" />
<!-- 自定义列 -->
<cl-column-custom
:columns="Table?.columns"
:ref="setRefs('columnCustom')"
/>
<!-- 高级搜索按钮 -->
<cl-adv-btn />
</cl-row>
<!-- 关键字搜索 -->
<cl-search-key placeholder="搜索姓名、手机号" :width="250" />
<cl-row>
<!-- 表格 -->
<cl-table ref="Table" show-summary :summary-method="onSummaryMethod">
<!-- 展开信息 -->
<template #column-detail="{ scope }">
<div style="padding: 0 10px">
<el-descriptions border :column="3">
<el-descriptions-item label="ID">
{{ scope.row.id }}
</el-descriptions-item>
<!-- 高级搜索按钮 -->
<cl-adv-btn />
</cl-row>
<el-descriptions-item label="姓名">
{{ scope.row.name }}
</el-descriptions-item>
<cl-row>
<!-- 表格 -->
<cl-table
ref="Table"
show-summary
:summary-method="onSummaryMethod"
:auto-height="false"
>
<!-- 展开信息 -->
<template #column-detail="{ scope }">
<div style="padding: 0 10px">
<el-descriptions border :column="3">
<el-descriptions-item label="ID">
{{ scope.row.id }}
</el-descriptions-item>
<el-descriptions-item label="存款">
{{ scope.row.wages }}
</el-descriptions-item>
<el-descriptions-item label="姓名">
{{ scope.row.name }}
</el-descriptions-item>
<el-descriptions-item label="出生年月">
{{ scope.row.createTime }}
</el-descriptions-item>
</el-descriptions>
</div>
</template>
<el-descriptions-item label="存款">
{{ scope.row.wages }}
</el-descriptions-item>
<!-- 自定义列 -->
<template #column-wages="{ scope }">
<span>{{ scope.row.wages }}🤑</span>
</template>
</cl-table>
</cl-row>
<el-descriptions-item label="出生年月">
{{ scope.row.createTime }}
</el-descriptions-item>
</el-descriptions>
</div>
</template>
<cl-row>
<cl-flex1 />
<!-- 自定义列 -->
<template #column-wages="{ scope }">
<span>{{ scope.row.wages }}🤑</span>
</template>
</cl-table>
</cl-row>
<!-- 分页 -->
<cl-pagination />
</cl-row>
<cl-row>
<cl-flex1 />
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
<!-- 分页 -->
<cl-pagination />
</cl-row>
<!-- 高级搜索 -->
<cl-adv-search ref="AdvSearch" />
</cl-crud>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
<!-- 高级搜索 -->
<cl-adv-search ref="AdvSearch" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script lang="tsx" name="demo-crud" setup>
import { useCrud, useUpsert, useTable, useAdvSearch, setFocus, useSearch } from "@cool-vue/crud";
import { useDict } from "/$/dict";
import { reactive } from "vue";
import { reactive, ref } from "vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { useCool } from "/@/cool";
import FormBtn from "../components/form-btn.vue";
import SelectUser from "../components/select-user.vue";
//
const { service, refs, setRefs } = useCool();
@ -140,6 +165,7 @@ const Crud = useCrud(
(app) => {
// Crud
app.refresh({
size: 10
// status: 1 //
});
}
@ -153,11 +179,6 @@ function refresh(params?: any) {
//
// <Eps.UserInfoEntity>prop data
const Upsert = useUpsert<Eps.UserInfoEntity>({
dialog: {
height: "600px", //
width: "1000px" //
},
items: [
//
{
@ -169,10 +190,6 @@ const Upsert = useUpsert<Eps.UserInfoEntity>({
label: "基础信息",
value: "base"
},
{
label: "选择用户",
value: "select"
},
{
label: "其他配置",
value: "other"
@ -214,8 +231,8 @@ const Upsert = useUpsert<Eps.UserInfoEntity>({
};
},
{
prop: "user",
group: "base",
prop: "user",
component: {
name: "cl-form-card",
props: {
@ -241,8 +258,8 @@ const Upsert = useUpsert<Eps.UserInfoEntity>({
]
},
{
prop: "contact",
group: "base",
prop: "contact",
component: {
name: "cl-form-card",
props: {
@ -268,24 +285,10 @@ const Upsert = useUpsert<Eps.UserInfoEntity>({
}
]
},
{
label: "",
prop: "userIds",
group: "select",
props: {
labelWidth: "0px"
},
required: true,
component: {
vm: SelectUser
}
},
{
group: "other",
label: "工作",
prop: "occupation",
group: "other",
component: {
name: "el-tree-select",
props: {
@ -295,9 +298,9 @@ const Upsert = useUpsert<Eps.UserInfoEntity>({
}
},
{
group: "other",
label: "身份证照片",
prop: "idCardPic",
group: "other",
component: {
name: "cl-upload",
props: {
@ -566,4 +569,10 @@ const Search = useSearch({
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,145 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">base</el-tag>
<span>起步</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['crud/base.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="起步" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-refresh-btn />
<cl-add-btn />
<cl-multi-delete-btn />
<cl-flex1 />
<cl-search-key />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { useCool } from "/@/cool";
const { service } = useCool();
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
// test service
service: "test"
},
(app) => {
//
app.refresh({
size: 10,
status: 1
});
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
type: "selection"
},
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op",
buttons: ["edit", "delete"]
}
]
});
// cl-upsert
const Upsert = useUpsert({
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input"
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,164 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">dict</el-tag>
<span>修改文案 / 接口</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['crud/dict.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="修改文案 / 接口" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-refresh-btn />
<cl-add-btn />
<cl-multi-delete-btn />
<cl-flex1 />
<cl-search-key />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
// serviceservice.base.sys.user
service: "test",
//
dict: {
//
// page getUserList
api: {
list: "list",
add: "add",
update: "update",
delete: "delete",
info: "info",
page: "page"
},
//
label: {
op: "操作",
add: "添加",
delete: "移除",
multiDelete: "批量移除",
update: "修改",
refresh: "刷新",
info: "详情"
}
}
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
type: "selection"
},
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op",
buttons: ["edit", "delete"]
}
]
});
// cl-upsert
const Upsert = useUpsert({
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input"
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,182 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">event</el-tag>
<span>事件监听</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['crud/event.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="事件监听" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-refresh-btn />
<cl-add-btn />
<cl-multi-delete-btn />
<cl-flex1 />
<cl-search-key />
</cl-row>
<cl-row>
<cl-table ref="Table">
<!-- 自定义按钮 -->
<template #slot-btn="{ scope }">
<el-button @click="onEvent(scope.row)">自定义事件</el-button>
</template>
</cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { useCool } from "/@/cool";
import { ElMessage } from "element-plus";
const { service } = useCool();
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
// service
service: "test",
// Crud.value.refresh()
onRefresh(params, { next }) {
// 使 next(params)
next({
...params,
status: 1
});
},
//
onDelete(selection, { next }) {
// ids
next({
ids: selection.map((e) => e.id)
});
}
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
type: "selection"
},
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op",
width: 300,
buttons: ["edit", "delete", "slot-btn"]
}
]
});
// cl-upsert
const Upsert = useUpsert({
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input"
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
// Crud
function onEvent(row: any) {
ElMessage.info("自定义打开新增");
//
Crud.value?.rowAdd();
//
// Crud.value?.rowEdit(row);
//
// Crud.value?.rowDelete(row);
//
// Crud.value?.getParams();
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,185 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">service</el-tag>
<span>Service 配置</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['crud/service.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="Service 配置" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-refresh-btn />
<cl-add-btn />
<cl-multi-delete-btn />
<cl-flex1 />
<cl-search-key />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useCool } from "/@/cool";
//service
const { service } = useCool();
console.log("service", service);
// cl-crud
const Crud = useCrud(
{
// serviceservice.demo.goods
// service.demo.goods.page
service: service.demo.goods
// 1 service
// /src/modules/demo/service/test.ts
// modules/*/service/ service
// service: service.test
// 2
// service: {
// page(params: any) {
// // params
// // Promise
// return Promise.resolve({
// list: [],
// pagination: {
// total: 1,
// page: 1,
// size: 20
// }
// });
// }
// // adddeleteupdateinfolist
// }
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
type: "selection"
},
{
label: "商品名称",
prop: "title",
minWidth: 140
},
{
label: "价格",
prop: "price",
minWidth: 140
},
{
label: "主图",
prop: "mainImage",
minWidth: 140,
component: {
name: "cl-image",
props: {
size: 60
}
}
},
{
label: "描述",
prop: "description",
minWidth: 200,
showOverflowTooltip: true
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op",
buttons: ["edit", "delete"]
}
]
});
// cl-upsert
const Upsert = useUpsert({
items: [
{
label: "商品名称",
prop: "title",
required: true,
component: {
name: "el-input"
}
},
{
label: "价格",
prop: "price",
required: true,
component: {
name: "el-input-number"
}
},
{
label: "主图",
prop: "mainImage",
required: true,
component: {
name: "cl-upload"
}
},
{
label: "描述",
prop: "description",
component: {
name: "el-input",
props: {
type: "textarea",
rows: 4
}
}
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,118 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">children</el-tag>
<span>层级显示</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/children.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
const Form = useForm();
function open() {
Form.value?.open({
title: "层级显示",
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "年龄",
prop: "age",
value: 18,
component: {
name: "el-input-number"
}
},
//
{
component: {
//使 cl-form-card 使
name: "cl-form-card",
props: {
//
label: "基础信息",
// true
expand: true
}
},
children: [
{
label: "账号",
prop: "account",
component: {
name: "el-input"
}
},
{
label: "密码",
prop: "password",
component: {
name: "el-input"
}
}
]
},
//
{
component: {
name: "cl-form-card",
props: {
label: "其他信息",
expand: false
}
},
children: [
{
label: "身份证",
prop: "idcard",
component: {
name: "el-input"
}
},
{
label: "学校",
prop: "school",
component: {
name: "el-input"
}
},
{
label: "专业",
prop: "major",
component: {
name: "el-input"
}
}
]
}
],
on: {
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -0,0 +1,106 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">component</el-tag>
<span>组件渲染</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code
:files="[
'form/component/index.vue',
'form/component/select-labels.vue',
'form/component/select-status.vue',
'form/component/select-work.vue'
]"
/>
<!-- 自定义表单组件 -->
<cl-form ref="Form">
<!-- 年龄插槽 -->
<template #slot-age="{ scope }">
<!-- scope 为表单值 -->
<el-input-number v-model="scope.age" :min="18" :max="100"></el-input-number>
</template>
</cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
import { ElMessage } from "element-plus";
import SelectWork from "./select-work.vue";
import SelectLabels from "./select-labels.vue";
import SelectStatus from "./select-status.vue";
const Form = useForm();
function open() {
Form.value?.open({
title: "组件配置",
items: [
{
label: "昵称",
prop: "nickname",
// 1便
component: {
// element-plus el-inputel-date-picker
name: "el-input"
}
},
{
label: "年龄",
prop: "age",
// 2
component: {
// "slot-"
name: "slot-age"
}
},
// -- start 3
{
label: "工作",
prop: "work",
component: {
//
vm: SelectWork
}
},
{
label: "标签",
prop: "labels",
value: [],
component: {
// scope[prop]
vm: SelectLabels
}
},
{
label: "状态",
prop: "status",
value: 1,
component: {
// useForm
vm: SelectStatus
}
}
// -- end
],
on: {
submit(data, { close }) {
ElMessage.info(
`${data.name || "无名"}${data.age || 18}岁)工作:${data.work || "无"}`
);
close();
}
}
});
}
</script>

View File

@ -0,0 +1,38 @@
<template>
<!--很重要直接绑定表单值 scope[prop] -->
<!-- !符号只是为了类型提示不错误 -->
<el-select v-model="scope[prop!]" multiple>
<el-option
v-for="(item, index) in list"
:key="index"
:label="item.label"
:value="item.label"
/>
</el-select>
</template>
<!--很重要必须要有name避免注册后和其他冲突 -->
<script setup lang="ts" name="select-labels">
import { ref } from "vue";
const props = defineProps({
scope: null, //
prop: String // prop
});
//
const list = ref<{ label: string; value: string }[]>([
{
label: "倒茶",
value: "倒茶" // 使label1234id
},
{
label: "设计",
value: "设计"
},
{
label: "开发",
value: "开发"
}
]);
</script>

View File

@ -0,0 +1,43 @@
<template>
<!--很重要直接绑定status或者使用 form[prop!] -->
<el-radio-group v-model="form.status">
<el-radio v-for="(item, index) in list" :key="index" :label="item.value">
{{ item.label }}
</el-radio>
</el-radio-group>
</template>
<!--很重要必须要有name避免注册后和其他冲突 -->
<script setup lang="ts" name="select-status">
import { useForm } from "@cool-vue/crud";
import { computed, ref } from "vue";
const props = defineProps({
scope: null, //
prop: String // prop
});
// 使 useForm
// Form.value?.submitForm.value?.close
// Form.value?.form
const Form = useForm();
//
const form = computed(() => Form.value?.form || {});
//
const list = ref<{ label: string; value: number }[]>([
{
label: "很好",
value: 1
},
{
label: "不舒服",
value: 2
},
{
label: "要嘎了",
value: 3
}
]);
</script>

View File

@ -0,0 +1,59 @@
<template>
<el-select v-model="active" @change="onChange">
<el-option
v-for="(item, index) in list"
:key="index"
:label="item.label"
:value="item.label"
/>
</el-select>
</template>
<!-- 很重要必须要有name避免注册后和其他冲突 -->
<script setup lang="ts" name="select-work">
import { ref, watch } from "vue";
const props = defineProps({
modelValue: Number
});
const emit = defineEmits(["update:modelValue", "change"]);
//
//
const active = ref();
//
const list = ref<{ label: string; value: string }[]>([
{
label: "倒茶",
value: "倒茶" // 使label1234id
},
{
label: "设计",
value: "设计"
},
{
label: "开发",
value: "开发"
}
]);
//
function onChange(val: string) {
emit("update:modelValue", val);
emit("change", val);
}
//使
watch(
() => props.modelValue,
(val) => {
//
active.value = val;
},
{
immediate: true
}
);
</script>

View File

@ -0,0 +1,122 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">config</el-tag>
<span>参数配置</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/config.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form">
<!-- 按钮插槽 -->
<template #slot-btns>
<el-button type="danger">按钮插槽</el-button>
</template>
</cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
import { ElMessage } from "element-plus";
const Form = useForm();
function open() {
Form.value?.open({
title: "参数配置",
//
isReset: false,
//
form: {
nickName: "神仙都没用"
},
//
props: {
//
labelWidth: "120px",
//
labelPosition: "top"
},
//
height: "60vh",
// 50%
width: "60%",
//
dialog: {
//
hideHeader: false,
// ["fullscreen", "close"]
// fullscreen
// close
controls: ["close"]
},
//
op: {
//
justify: "flex-end",
//
saveButtonText: "提交",
//
closeButtonText: "关闭",
//
hidden: false,
//
buttons: [
//
{
label: "自定义按钮",
onClick() {
ElMessage.success("自定义按钮点击");
}
},
// close
"close",
// save
"save",
// 使 template cl-form
"slot-btns"
]
},
//
items: [
{
label: "昵称",
prop: "nickName",
component: {
name: "el-input"
}
}
],
//
on: {
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -1,44 +1,59 @@
<template>
<el-button type="primary" @click="open">自定义表单</el-button>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">crud</el-tag>
<span>内嵌CRUD</span>
</div>
<cl-form ref="Form">
<template #slot-crud>
<cl-crud ref="Crud" border>
<cl-row>
<!-- 刷新按钮 -->
<cl-refresh-btn />
<!-- 新增按钮 -->
<cl-add-btn />
<!-- 删除按钮 -->
<cl-multi-delete-btn />
<cl-flex1 />
<!-- 关键字搜索 -->
<cl-search-key placeholder="搜索姓名、手机号" />
</cl-row>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/crud.vue']" />
<cl-row>
<!-- 数据表格 -->
<cl-table ref="Table" />
</cl-row>
<!-- 自定义表单组件 -->
<cl-form ref="Form">
<template #slot-crud>
<cl-crud ref="Crud" border>
<cl-row>
<!-- 刷新按钮 -->
<cl-refresh-btn />
<!-- 新增按钮 -->
<cl-add-btn />
<!-- 删除按钮 -->
<cl-multi-delete-btn />
<cl-flex1 />
<!-- 关键字搜索 -->
<cl-search-key placeholder="搜索姓名、手机号" />
</cl-row>
<cl-row>
<cl-flex1 />
<!-- 分页控件 -->
<cl-pagination />
</cl-row>
<cl-row>
<!-- 数据表格 -->
<cl-table ref="Table" />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</template>
</cl-form>
<cl-row>
<cl-flex1 />
<!-- 分页控件 -->
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</template>
</cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script lang="ts" setup>
<script setup lang="ts">
import { useCrud, useForm, useTable, useUpsert, setFocus } from "@cool-vue/crud";
import { useCool } from "/@/cool";
const { refs, setRefs, service } = useCool();
const { service } = useCool();
// cl-upsert
const Upsert = useUpsert({
@ -100,7 +115,7 @@ const Form = useForm();
function open() {
Form.value?.open({
title: "自定义表单",
title: "内嵌CRUD",
props: {
labelPosition: "top"
},
@ -110,11 +125,10 @@ function open() {
},
items: [
{
label: "获取 ref打开后聚焦",
label: "姓名",
prop: "name",
component: {
name: "el-input",
ref: setRefs("name"),
props: {
placeholder: "请填写姓名"
}
@ -131,13 +145,7 @@ function open() {
}
}
],
op: {
// buttons: ["save"]
},
on: {
open() {
refs.name.focus();
},
submit() {
Form.value?.close();
}

View File

@ -0,0 +1,64 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">disabled</el-tag>
<span>组件禁用</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/disabled.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
const Form = useForm();
function open() {
Form.value?.open({
title: "组件禁用",
items: [
{
label: "账号",
prop: "account",
component: {
name: "el-input",
props: {
// boolean element
disabled: true
}
}
},
{
label: "密码",
prop: "password",
component: {
name: "el-input"
}
}
],
on: {
open() {
// setProps disabled, 1.5s
setTimeout(() => {
Form.value?.setProps("password", { disabled: true });
}, 1500);
},
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -0,0 +1,93 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">event</el-tag>
<span>组件事件</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/event.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
import { ElMessage } from "element-plus";
const Form = useForm();
function open() {
Form.value?.open({
title: "组件事件",
items: [
{
label: "账号",
prop: "account",
component: {
name: "el-input",
props: {
// emit on[name] onChangeonInputonBlur
//
onBlur() {
ElMessage.info("账号检查中");
}
}
}
},
{
label: "是否实名",
prop: "status",
value: 1,
component: {
name: "el-radio-group",
options: [
{
label: "关闭",
value: 0
},
{
label: "开启",
value: 1
}
],
props: {
//
onChange(val: number) {
if (val == 1) {
//
Form.value?.showItem("idcard");
} else {
//
Form.value?.hideItem("idcard");
//
Form.value?.setForm("idcard", undefined);
}
}
}
}
},
{
label: "身份证",
prop: "idcard",
component: {
name: "el-input"
}
}
],
on: {
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -0,0 +1,105 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">group</el-tag>
<span>分组显示</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/group.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
const Form = useForm();
function open() {
Form.value?.open({
title: "分组显示",
items: [
{
// tabs
type: "tabs",
props: {
//
type: "card",
// { label, value }
labels: [
{
label: "基础信息", //
value: "base" //
},
{
label: "认证信息",
value: "auth"
}
]
}
},
//
{
group: "base", //
label: "账号",
prop: "account",
required: true,
component: {
name: "el-input"
}
},
{
group: "base", //
label: "密码",
prop: "password",
required: true,
component: {
name: "el-input"
}
},
// group = other
{
group: "auth", //
label: "身份证",
prop: "idcard",
required: true,
component: {
name: "el-input"
}
},
{
group: "auth", //
label: "学校",
prop: "school",
component: {
name: "el-input"
}
},
{
group: "auth", //
label: "专业",
prop: "major",
component: {
name: "el-input"
}
}
],
on: {
//
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -0,0 +1,77 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">hidden</el-tag>
<span>隐藏/显示</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/hidden.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
const Form = useForm();
function open() {
Form.value?.open({
title: "隐藏/显示",
items: [
{
label: "状态",
prop: "status",
value: 0,
component: {
name: "el-radio-group",
options: [
{
label: "关闭",
value: 0
},
{
label: "开启",
value: 1
}
]
}
},
{
label: "账号",
prop: "account",
component: {
name: "el-input"
}
},
{
//
hidden({ scope }) {
// scope
// boolean /
return scope.status != 1;
},
label: "密码",
prop: "password",
component: {
name: "el-input"
}
}
],
on: {
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -0,0 +1,116 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">layout</el-tag>
<span>布局</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/layout.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
const Form = useForm();
function open() {
Form.value?.open({
title: "布局",
items: [
{
//spanhttps://element-plus.gitee.io/zh-CN/component/layout.html
// 使 1/24 24
span: 12,
label: "昵称",
prop: "nickname",
component: {
name: "el-input"
}
},
{
span: 12,
label: "手机号",
prop: "phone",
component: {
name: "el-input",
props: {
maxlength: 11
}
}
},
{
//flex使
flex: false,
label: "标签",
prop: "label",
component: {
name: "el-select",
options: [
{
label: "javascript",
value: 1
},
{
label: "vue",
value: 2
},
{
label: "html",
value: 3
},
{
label: "css",
value: 4
}
]
}
},
{
label: "状态",
prop: "status",
value: 1,
component: {
name: "el-radio-group",
options: [
{
label: "开启",
value: 1
},
{
label: "关闭",
value: 0
}
]
}
},
{
label: "备注",
prop: "remark",
component: {
name: "el-input",
props: {
type: "textarea",
rows: 4
}
}
}
],
on: {
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -0,0 +1,83 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">open</el-tag>
<span>起步</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/open.vue']" />
<!-- 自定义表单组件 -->
<!--很重要ref 一定要对应 useForm 定义的值 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
const Form = useForm();
function open() {
Form.value?.open({
title: "起步",
items: [
{
label: "昵称",
// prop
prop: "nickname",
//
component: {
// element-plus el-inputel-date-picker
name: "el-input",
// clearableplaceholder
// emit on[name] onChangeonInputonBlur
props: {
placeholder: "请输入昵称",
clearable: true,
onChange(value: string) {}
}
}
},
{
label: "年龄",
prop: "age",
component: {
name: "el-input-number"
},
//
value: 18
}
],
on: {
//
open() {},
// 使 done()
close(action, done) {
// action "save" | "close"
// done
done();
},
//
submit(data, { done, close }) {
// data
// done
// close
close();
}
}
});
}
</script>

View File

@ -0,0 +1,172 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">options</el-tag>
<span>选项框配置</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/options.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
import { computed, reactive } from "vue";
const Form = useForm();
// any { user: [] as any[] }
const options = reactive<{ [key: string]: { label: string; value: any }[] }>({
user: []
});
function open() {
Form.value?.open({
title: "选项框配置",
items: [
{
label: "下拉框",
prop: "select",
component: {
name: "el-select",
props: {
clearable: true //
},
options: [
{
label: "javascript",
value: 1
},
{
label: "vue",
value: 2
},
{
label: "html",
value: 3
},
{
label: "css",
value: 4
}
]
}
},
{
label: "单选框",
prop: "radio",
value: 1,
component: {
name: "el-radio-group",
options: [
{
label: "手机",
value: 1
},
{
label: "电脑",
value: 2
},
{
label: "电视",
value: 3
}
]
}
},
{
label: "多选框",
prop: "checkbox",
value: [2, 3],
component: {
name: "el-checkbox-group",
options: [
{
label: "咖啡",
value: 1
},
{
label: "汉堡",
value: 2
},
{
label: "炸鸡",
value: 3
},
{
label: "奶茶",
value: 4
}
]
}
},
{
label: "动态配置1",
prop: "d1",
component: {
name: "el-select",
// 1 on.open options
options: []
}
},
{
label: "动态配置2",
prop: "d2",
component: {
name: "el-select",
// 2使 computed options
options: computed(() => options.user)
}
}
],
on: {
open() {
// 1.5s
setTimeout(() => {
// 1使 setOptions
// d1 prop
Form.value?.setOptions("d1", [
{
label: "😊",
value: 1
},
{
label: "😭",
value: 2
},
{
label: "😘",
value: 3
}
]);
// 2 options.user computed
options.user = [
{
label: "💰",
value: 1
},
{
label: "🚗",
value: 2
}
];
}, 1500);
},
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -0,0 +1,112 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">plugin</el-tag>
<span>插件的使用</span>
</div>
<div class="c">
<el-button @click="open('manager')">管理者</el-button>
<el-button @click="open('user')">用户</el-button>
<demo-code :files="['form/plugin/index.vue', 'form/plugin/role.ts']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { setFocus, useForm } from "@cool-vue/crud";
import { setRole } from "./role";
const Form = useForm();
function open(role: string) {
Form.value?.open(
{
title: "插件的使用",
items: [
{
label: "姓名",
prop: "name",
required: true,
component: {
name: "el-input"
}
},
{
// role
role: "user",
label: "面试职位",
prop: "work",
value: 1,
component: {
name: "el-radio-group",
options: [
{
label: "前端开发",
value: 1
},
{
label: "后端开发",
value: 2
},
{
label: "UI设计",
value: 3
}
]
}
},
{
role: "user",
label: "期望薪资",
prop: "salary",
value: 5000,
component: {
name: "el-input-number",
props: {
min: 2000,
max: 100000
}
}
},
{
role: "manager",
label: "入职时间",
prop: "date",
component: {
name: "el-date-picker"
}
},
{
role: "manager",
label: "负责人",
prop: "head",
component: {
name: "el-input"
}
}
],
on: {
submit(data, { done, close }) {
close();
}
}
},
[
// item
setFocus(),
//
setRole(role)
]
);
}
</script>

View File

@ -0,0 +1,20 @@
/**
*
* @param role
* @returns
*/
export function setRole(role?: string): ClForm.Plugin {
return ({ exposed }) => {
function deep(arr: ClForm.Item[]) {
arr.forEach((e) => {
if (e.role) {
e.hidden = e.role != role;
}
deep(e.children || []);
});
}
deep(exposed.config.items);
};
}

View File

@ -0,0 +1,75 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">required</el-tag>
<span>必填项配置</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/required.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form"></cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
const Form = useForm();
function open() {
Form.value?.open({
title: "必填项配置",
items: [
{
label: "昵称",
prop: "nickname",
component: {
name: "el-input"
},
//
required: true
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input",
props: {
maxlength: 11
}
},
//
// https://element-plus.gitee.io/zh-CN/component/form.html
// https://github.com/yiminghe/async-validator
rules: [
{
required: true,
validator: (rule, value, callback) => {
if (value === "") {
callback(new Error("手机号不能为空"));
} else if (!/^1[3456789]\d{9}$/.test(value)) {
callback(new Error("手机号格式错误"));
} else {
callback();
}
}
}
]
}
],
on: {
submit(data, { close }) {
close();
}
}
});
}
</script>

View File

@ -0,0 +1,123 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">rules</el-tag>
<span>添加/删除表单项</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['form/rules.vue']" />
<!-- 自定义表单组件 -->
<cl-form ref="Form">
<template #slot-cert="{ scope }">
<div class="cert">
<!--很重要proprules 配置格式如下 -->
<el-form-item
v-for="(item, index) in scope.cert"
:key="index"
:label="`证书${index + 1}`"
:prop="`cert.${index}.label`"
:rules="{
message: `请填写证书${index + 1}`,
required: true
}"
>
<div class="row">
<!-- 输入框 -->
<el-input v-model="item.label" placeholder="请填写证书"></el-input>
<!-- 删除行 -->
<el-icon @click="rowDel(index)">
<delete />
</el-icon>
</div>
</el-form-item>
<!-- 添加行 -->
<el-row type="flex" justify="end">
<el-button @click="rowAdd()">添加证书</el-button>
</el-row>
</div>
</template>
</cl-form>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useForm } from "@cool-vue/crud";
import { Delete } from "@element-plus/icons-vue";
const Form = useForm();
function open() {
Form.value?.open({
title: "添加/删除表单项",
items: [
{
label: "昵称",
prop: "nickname",
component: {
name: "el-input"
},
required: true
},
{
prop: "cert",
//
value: [
{
label: ""
}
],
component: {
name: "slot-cert"
}
}
],
on: {
submit(data, { close }) {
close();
}
}
});
}
function rowAdd() {
Form.value?.form.cert.push({
label: ""
});
}
function rowDel(index: number) {
Form.value?.form.cert.splice(index, 1);
}
</script>
<style lang="scss" scoped>
.cert {
.row {
display: flex;
align-items: center;
.el-input {
flex: 1;
margin-right: 10px;
}
.el-icon {
cursor: pointer;
&:hover {
color: red;
}
}
}
}
</style>

View File

@ -63,7 +63,7 @@
<script lang="ts" setup name="select-user">
import { useCrud, useForm, useTable } from "@cool-vue/crud";
import { useCool } from "/@/cool";
import { PropType, computed, nextTick, reactive, ref, watch } from "vue";
import { type PropType, computed, nextTick, reactive, ref, watch } from "vue";
import { cloneDeep } from "lodash-es";
//

View File

@ -0,0 +1,168 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">tips</el-tag>
<span>代码类型提示</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['other/tips.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="代码类型提示" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-refresh-btn />
<cl-add-btn />
<cl-multi-delete-btn />
<cl-flex1 />
<cl-search-key />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useCool } from "/@/cool";
const { service } = useCool();
// cl-crud
const Crud = useCrud(
{
service: service.demo.goods
},
(app) => {
app.refresh();
}
);
// cl-table
// <Eps.DemoGoodsEntity><{ title: string; price: number }>
const Table = useTable<Eps.DemoGoodsEntity>({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
type: "selection"
},
{
label: "商品标题",
prop: "title", // DemoGoodsEntity
minWidth: 140
},
{
label: "主图",
prop: "mainImage",
minWidth: 140,
component: {
name: "cl-image",
props: {
size: 60
}
}
},
{
label: "价格",
prop: "price",
minWidth: 120
},
{
label: "库存",
prop: "stock",
minWidth: 120
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op"
}
]
});
// cl-upsert
// <Eps.DemoGoodsEntity><{ title: string; price: number }>
const Upsert = useUpsert<Eps.DemoGoodsEntity>({
items: [
{
label: "商品标题",
prop: "title", // DemoGoodsEntity
component: {
name: "el-input"
}
},
{
label: "主图",
prop: "mainImage",
component: {
name: "cl-upload"
}
},
{
label: "价格",
prop: "price",
hook: "number",
component: {
name: "el-input-number",
props: {
min: 0.01,
max: 10000
}
}
},
{
label: "库存",
prop: "stock",
component: {
name: "el-input-number",
props: {
min: 0,
max: 1000
}
}
}
],
onSubmit(data, { next }) {
// data DemoGoodsEntity
next({
...data,
title: data.title
});
}
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,28 @@
.tsx-list {
.item {
display: flex;
align-items: center;
justify-content: space-between;
border: 1px solid var(--el-border-color);
padding: 10px;
margin-bottom: 10px;
cursor: pointer;
border-radius: 4px;
.el-icon {
display: none;
}
&:hover {
background-color: var(--el-bg-color-page);
}
&.is-active {
color: var(--el-color-primary);
.el-icon {
display: block;
}
}
}
}

View File

@ -0,0 +1,107 @@
import { defineComponent, ref } from "vue";
import { Check } from "@element-plus/icons-vue";
import "./index.scss";
interface Item {
name: string;
value: number;
}
export default defineComponent({
emits: ["checked"],
setup(props, { emit, expose, slots }) {
// 列表数据
const list = ref<Item[]>([
{
name: "鸡腿堡",
value: 1
},
{
name: "牛肉堡",
value: 2
}
]);
// 选择值
const active = ref();
// 是否可见
const visible = ref(false);
// 打开
function open() {
visible.value = true;
}
// 选择
function toCheck(item: Item) {
active.value = item.value;
// 自定义事件
emit("checked", item);
}
// 暴露方法和变量,使上级可以使用 ref 的方式来调用
expose({
toCheck
});
// 必须返回一个方法
return () => {
return (
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">
tsx
</el-tag>
<span>tsx示例</span>
</div>
<div class="c">
<el-button onClick={open}></el-button>
<demo-code files={["other/tsx/index.tsx"]} />
{/* ref 的绑定值必须 .value */}
<cl-dialog v-model={visible.value} title="tsx示例">
<div class="tsx-list">
{/* 循环的使用 */}
{list.value.map((item) => {
// 插槽的使用
return slots.default ? (
slots.default(item)
) : (
<div
// 动态样式的使用
class={[
"item",
{ "is-active": item.value == active.value }
]}
// 事件的使用
onClick={() => toCheck(item)}
>
<span>{item.name}</span>
<el-icon>
<Check />
</el-icon>
</div>
);
})}
</div>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
);
};
}
// 不推荐用该方法,在 setup 中返回模板信息
// render() {
// return <div></div>;
// }
});

View File

@ -0,0 +1,143 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">base</el-tag>
<span>起步</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['search/base.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="起步" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!--很重要搜索组件 -->
<cl-search ref="Search" :reset-btn="true" />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useSearch, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
// cl-search
// cl-form
const Search = useSearch({
// cl-form
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input",
props: {
clearable: true,
//
onChange(val: string) {
refresh({
name: val,
page: 1
});
}
}
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input",
props: {
clearable: true
}
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
function refresh(params?: any) {
Crud.value?.refresh(params);
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,176 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">custom</el-tag>
<span>自定义</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['search/custom.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="自定义" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!--很重要搜索组件 -->
<cl-search
ref="Search"
:reset-btn="true"
:on-load="onLoad"
:on-search="onSearch"
>
<!-- 自定义按钮 -->
<template #buttons="scope">
<el-button @click="toSearch(scope)">自定义按钮</el-button>
</template>
</cl-search>
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useSearch, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { ElMessage } from "element-plus";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
// cl-search
// cl-form
const Search = useSearch({
// cl-form
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input",
props: {
clearable: true,
//
onChange(val: string) {
refresh({
name: val,
page: 1
});
}
}
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input",
props: {
clearable: true
}
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
function refresh(params?: any) {
Crud.value?.refresh(params);
}
// cl-search
function onLoad(data: any) {
data.name = "白小纯";
}
// cl-search onSearch 使 next
function onSearch(data: any, { next }: { next: (data: any) => void }) {
ElMessage.info("开始搜索");
//
next(data);
}
// data
function toSearch(data: any) {
ElMessage.info("自定义搜索");
refresh({
page: 1,
...data
});
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,109 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">base</el-tag>
<span>起步</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/base.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="起步" width="80%">
<!--很重要需要包含在 cl-crud 组件内 -->
<cl-crud ref="Crud">
<cl-row>
<!-- 参数文档查看https://element-plus.org/zh-CN/component/table.html#table-%E5%B1%9E%E6%80%A7 -->
<cl-table ref="Table" stripe></cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
// cl-crud
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
//
// autoHeight: false maxHeight: 500
autoHeight: false,
//
contextMenu: ["refresh"],
// columns
// el-table-column https://element-plus.org/zh-CN/component/table.html#table-column-%E5%B1%9E%E6%80%A7
columns: [
{
//
type: "selection"
//
// type: "index"
},
{
//
label: "姓名",
//
prop: "name",
//
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
//
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
// desc, asc
sortable: "desc"
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,98 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">children</el-tag>
<span>多级表头</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/children.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="多级表头" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "用户信息",
prop: "baseInfo",
minWidth: 250,
// children
children: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
}
]
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,108 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">column-custom</el-tag>
<span>自定义列展示</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/column-custom.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="自定义列展示" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!--很重要组件配置设置为 Table columns也可以自定义 -->
<cl-column-custom :columns="Table?.columns" />
</cl-row>
<cl-row>
<cl-table ref="Table"></cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "状态",
prop: "status",
dict: [
{
label: "启用",
value: 1,
type: "success"
},
{
label: "禁用",
value: 0,
type: "danger"
}
],
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,108 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">component</el-tag>
<span>组件渲染</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/component/index.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="组件渲染" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table"></cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { ElMessage } from "element-plus";
import UserInfo from "./user-info.vue";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
type: "selection"
},
{
label: "姓名",
prop: "name",
minWidth: 140,
//
component: {
vm: UserInfo
}
},
{
label: "手机号",
prop: "phone",
minWidth: 140,
//
component: {
//
name: "el-input",
//
props: {
onChange(val) {
ElMessage.info(val);
}
}
}
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,30 @@
<template>
<div class="user-info">
<cl-avatar :size="36" />
<div class="det">
<p>{{ scope?.name }}</p>
</div>
</div>
</template>
<!-- name 必须填写且唯一 -->
<script setup lang="ts" name="user-info">
const props = defineProps({
prop: String, // prop
scope: null //
});
</script>
<style lang="scss" scoped>
.user-info {
display: flex;
align-items: center;
.det {
flex: 1;
margin-left: 10px;
text-align: left;
}
}
</style>

View File

@ -0,0 +1,191 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">context-menu</el-tag>
<span>右键菜单</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/context-menu.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="右键菜单">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table"></cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { ElMessage } from "element-plus";
import { EditPen, MoreFilled } from "@element-plus/icons-vue";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
// []
contextMenu: [
"refresh", //
"check", //
"edit", //
"delete", //
"info", //
"order-desc", // 使
"order-asc", // 使
{
label: "禁用状态",
disabled: true
},
{
label: "带图标",
prefixIcon: EditPen,
suffixIcon: MoreFilled
},
{
label: "超出隐藏,看我有很多字非常多",
ellipsis: true
},
{
label: "多层级",
children: [
{
label: "A",
children: [
{
label: "A-1",
callback(done) {
ElMessage.success("点击了A-1");
done();
}
}
]
},
{
label: "B"
},
{
label: "C"
}
]
},
// row
// column
// event
(row, column, event) => {
//
return {
label: "自定义2",
callback(done) {
ElMessage.info("获取中");
setTimeout(() => {
ElMessage.success(`Ta 是${row.name}`);
// callback
done();
}, 500);
}
};
}
],
columns: [
{
type: "selection"
},
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
// cl-upsert cl-upsert
const Upsert = useUpsert({
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input"
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,156 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">dict</el-tag>
<span>字典匹配</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/dict.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="字典匹配" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { computed, reactive, ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
const options = reactive({
occupation: [] as { label: string; value: any }[]
});
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
//
// 使 get /dict/list
dict: dict.get("occupation"),
// 使
dictColor: true,
minWidth: 140
},
{
label: "等级",
prop: "occupation",
//使 computed
dict: computed(() => options.occupation),
minWidth: 140
},
{
label: "状态",
prop: "status",
//
dict: [
{
label: "启用", //
value: 1, //
type: "success" // el-tag typesuccessdangerwarninginfo primary
},
{
label: "禁用",
value: 0,
type: "danger"
}
],
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
//
setTimeout(() => {
options.occupation = [
{
label: "A",
value: 0
},
{
label: "B",
value: 1
},
{
label: "C",
value: 2
},
{
label: "D",
value: 3
},
{
label: "E",
value: 4
},
{
label: "F",
value: 5
}
];
}, 1500);
}
</script>

View File

@ -0,0 +1,127 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">hidden</el-tag>
<span>隐藏/显示</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/hidden.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="隐藏/显示" width="80%">
<cl-crud ref="Crud">
<!--配置一个 tab -->
<el-tabs v-model="active">
<el-tab-pane label="员工" name="user"></el-tab-pane>
<el-tab-pane label="企业" name="company"></el-tab-pane>
</el-tabs>
<cl-row>
<!-- 使用方法 showColumn 显示 -->
<el-button @click="showColumn('account')">显示账号</el-button>
<!-- 使用方法 hideColumn 隐藏 -->
<el-button @click="hideColumn('account')">隐藏账号</el-button>
</cl-row>
<cl-row>
<cl-table ref="Table"></cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { computed, ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
// cl-crud
service: "test"
},
(app) => {
app.refresh();
}
);
const active = ref("user");
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "ID",
prop: "id",
minWidth: 140,
// hidden boolean Vue.ComputedRef<boolean>
hidden: computed(() => {
return active.value != "company";
})
},
{
label: "账号",
prop: "account",
minWidth: 140,
hidden: true // false
},
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
function hideColumn(prop: string) {
Table.value?.hideColumn(prop);
}
function showColumn(prop: string) {
Table.value?.showColumn(prop);
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,163 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">op</el-tag>
<span>操作栏</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/base.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="操作栏" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table">
<!-- 插槽的渲染方式 #[component.name] -->
<template #slot-btns="{ scope }">
<el-button
@click="
() => {
ElMessage.info(scope.row.name);
}
"
>插槽按钮</el-button
>
</template>
</cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!-- 新增编辑 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { ElMessage } from "element-plus";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
//type op
type: "op",
width: 410, //
//edit info cl-upsert
// edit service info cl-upsert
// info cl-upsert disabled
// delete service delete
buttons: [
"edit",
"info",
"delete",
{
label: "自定义",
onClick({ scope }) {
// scope { row, column, $index, store }
ElMessage.info("点击了自定义按钮");
}
},
"slot-btns"
]
//
// buttons() {
// return ['edit', 'info', 'delete']
// }
}
]
});
// cl-upsert cl-upsert
const Upsert = useUpsert({
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input"
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true, //
checkStrictly: true, //
options: dict.get("occupation") // 使
}
}
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,143 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">search</el-tag>
<span>表头搜索</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/search.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="表头搜索" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140,
//
search: {
isInput: false, // false
value: "", //
refreshOnChange: true, // falseservice page { page: 1, [prop]: }
//
component: {
name: "el-input",
props: {
placeholder: "搜索姓名"
}
}
}
},
{
label: "手机号",
prop: "phone",
minWidth: 140,
//
search: {
//
component: {
name: "el-input",
props: {
placeholder: "搜索手机号",
// change
onChange(val) {
Crud.value?.refresh({
page: 1,
phone: val
});
}
}
}
}
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140,
//
search: {
refreshOnChange: false, // cl-select onChange
component: {
name: "cl-select",
props: {
tree: true, //
checkStrictly: true, //
options: dict.get("occupation") // 使
}
}
}
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc",
//
search: {
component: {
name: "cl-date-picker", // cl-date-picker onChange
props: {
type: "date",
valueFormat: "YYYY-MM-DD"
}
}
}
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,110 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">selection</el-tag>
<span>多选框数据</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/selection.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="多选框数据" width="80%">
<cl-crud ref="Crud">
<cl-row>
<el-button @click="selectRow">选中2行</el-button>
<el-button :disabled="Table?.selection.length == 0" @click="clear"
>取消选择</el-button
>
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<el-text>已选 {{ Table?.selection.length }} </el-text>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh", "check"],
columns: [
{
type: "selection"
},
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
function selectRow() {
const [a, b] = Table.value?.data || [];
// 2
Table.value?.toggleRowSelection(a);
Table.value?.toggleRowSelection(b);
}
function clear() {
// https://element-plus.gitee.io/zh-CN/component/table.html#table-%E6%96%B9%E6%B3%95
Table.value?.clearSelection();
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,97 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">slot</el-tag>
<span>插槽的使用</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/slot.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="插槽的使用" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table">
<!--很重要必须与 prop 名保持一致格式column-[prop] -->
<template #column-name="{ scope }">
<cl-row type="flex" align="middle">
<cl-avatar :size="36" :style="{ marginRight: '10px' }" />
<el-text>{{ scope.row.name }}</el-text>
</cl-row>
</template>
<template #column-phone="{ scope }"> 📱{{ scope.row.phone }} </template>
</cl-table>
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
headerAlign: "left",
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,116 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">span-method</el-tag>
<span>合并行或列</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/span-method.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="合并行或列" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table" :span-method="onSpanMethod" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { TableColumnCtx } from "element-plus";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "存款",
prop: "wages",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
interface SpanMethodProps {
row: any;
column: TableColumnCtx<any>;
rowIndex: number;
columnIndex: number;
}
function onSpanMethod({ row, column, rowIndex, columnIndex }: SpanMethodProps) {
// { rowspan, colspan }
if (columnIndex === 0) {
if (rowIndex % 2 === 0) {
return {
rowspan: 2,
colspan: 1
};
} else {
return {
rowspan: 0,
colspan: 0
};
}
}
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,95 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">summary</el-tag>
<span>表尾合计行</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['table/summary.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="表尾合计行" width="80%">
<cl-crud ref="Crud">
<cl-row>
<cl-table ref="Table" show-summary :summary-method="getSummaries" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "存款",
prop: "wages",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
}
]
});
function getSummaries() {
return ["合计", "$" + Table.value?.data.reduce((a, b) => a + b.wages, 0)];
}
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,133 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">base</el-tag>
<span>起步</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['upsert/base.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="起步" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!-- 打开新增表单的按钮 -->
<cl-add-btn />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!--很重要新增编辑的表单组件 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op",
// edit
buttons: ["edit", "delete"]
}
]
});
// cl-upsert
// cl-form
const Upsert = useUpsert({
// cl-form
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input"
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,210 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">event</el-tag>
<span>事件</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['upsert/event.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="事件" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!-- 打开新增表单的按钮 -->
<cl-add-btn />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!--很重要新增编辑的表单组件 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { useCool } from "/@/cool";
const { service } = useCool();
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op",
// edit
buttons: ["edit", "delete"]
}
]
});
// cl-upsert
const Upsert = useUpsert({
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input"
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
],
//
//
onOpen() {
console.log("onOpen");
},
//
async onInfo(data, { next, done }) {
// onInfo next(data) service info
// next(data);
//
const res = await next({
id: data.id
});
done({
...res,
name: `[${res.name}]`
});
},
//
onOpened(data) {
//
if (Upsert.value?.mode == "update") {
//
data.phone += "000";
}
},
//
// data
// next
// done
// close
async onSubmit(data, { next, done, close }) {
// onSubmit next(data) service update/add
// next(data);
//
// 1
// next({
// ...data,
// status: 1,
// createTime: dayjs().format("YYYY-MM-DD")
// });
// 2
// userId
const userId = await service.test.info({ id: 1 });
//
const res = await next({
userId,
data
});
//
// console.log(res);
},
//
onClose(action, done) {
// action
console.log("action", action);
// 使 done
done();
},
//
onClosed() {
console.log("onClosed");
}
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,200 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">hook</el-tag>
<span>Hook的使用</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['upsert/hook/index.vue', 'upsert/hook/reg-pca2.ts']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="Hook的使用" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!-- 打开新增表单的按钮 -->
<cl-add-btn />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!--很重要新增编辑的表单组件 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "省市区",
prop: "pca",
formatter(row) {
return row.province ? row.province + "-" + row.city + "-" + row.district : "-";
},
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op",
buttons: ["edit", "delete"]
}
]
});
// cl-upsert
const Upsert = useUpsert({
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
{
label: "手机号",
prop: "phone",
component: {
name: "el-input"
}
},
{
label: "省市区",
prop: "pca2",
//hook
hook: {
bind(value, { form }) {
// 3
return [form.province, form.city, form.district];
},
submit(value, { form, prop }) {
// 3
const [province, city, district] = value || [];
form.province = province;
form.city = city;
form.district = district;
// prop
form[prop] = undefined;
}
},
// 使 ./reg-pca2.ts
// hook: "pca2",
component: {
name: "cl-distpicker"
}
},
{
label: "标签",
prop: "labels",
//使
hook: {
// labels 1,2,3
// labels ,
bind: ["split", "number"],
// labels
submit: ["join"]
},
component: {
name: "el-select",
props: {
multiple: true
},
options: [
{
label: "帅气",
value: 1
},
{
label: "多金",
value: 2
},
{
label: "有才华",
value: 3
}
]
}
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
]
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,14 @@
import { registerFormHook } from "@cool-vue/crud";
// 注册 hook
registerFormHook("pca2", (value, { method, form, prop }) => {
if (method == "bind") {
return [form.province, form.city, form.district];
} else {
const [province, city, district] = value || [];
form.province = province;
form.city = city;
form.district = district;
form[prop] = undefined;
}
});

View File

@ -0,0 +1,146 @@
<template>
<div class="scope">
<div class="h">
<el-tag size="small" effect="dark">mode</el-tag>
<span>不同模式</span>
</div>
<div class="c">
<el-button @click="open">预览</el-button>
<demo-code :files="['upsert/mode.vue']" />
<!-- 自定义表格组件 -->
<cl-dialog v-model="visible" title="不同模式" width="80%">
<cl-crud ref="Crud">
<cl-row>
<!-- 打开新增表单的按钮 -->
<cl-add-btn />
</cl-row>
<cl-row>
<cl-table ref="Table" />
</cl-row>
<cl-row>
<cl-flex1 />
<cl-pagination />
</cl-row>
<!--很重要新增编辑的表单组件 -->
<cl-upsert ref="Upsert" />
</cl-crud>
</cl-dialog>
</div>
<div class="f">
<span class="date">2024-01-01</span>
</div>
</div>
</template>
<script setup lang="ts">
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
import { ref } from "vue";
import { useDict } from "/$/dict";
import { ElMessage } from "element-plus";
const { dict } = useDict();
// cl-crud
const Crud = useCrud(
{
service: "test"
},
(app) => {
app.refresh();
}
);
// cl-table
const Table = useTable({
autoHeight: false,
contextMenu: ["refresh"],
columns: [
{
label: "姓名",
prop: "name",
minWidth: 140
},
{
label: "手机号",
prop: "phone",
minWidth: 140
},
{
label: "工作",
prop: "occupation",
dict: dict.get("occupation"),
minWidth: 140
},
{
label: "创建时间",
prop: "createTime",
minWidth: 160,
sortable: "desc"
},
{
type: "op",
width: 240,
buttons: ["info", "edit", "delete"]
}
]
});
// cl-upsert
const Upsert = useUpsert({
items: [
{
label: "姓名",
prop: "name",
component: {
name: "el-input"
}
},
//使 Upsert
() => {
return {
label: "手机号",
prop: "phone",
//
// hidden: Upsert.value?.mode == "add",
component: {
name: "el-input",
props: {
//
disabled: Upsert.value?.mode == "update"
}
}
};
},
{
label: "工作",
prop: "occupation",
component: {
name: "cl-select",
props: {
tree: true,
checkStrictly: true,
options: dict.get("occupation")
}
}
}
],
onOpen() {
ElMessage.info(`当前模式:` + Upsert.value?.mode);
}
});
const visible = ref(false);
function open() {
visible.value = true;
}
</script>

View File

@ -0,0 +1,288 @@
<template>
<div class="crud-demo">
<el-tabs type="card" v-model="active" @tab-change="onTabChange">
<el-tab-pane v-for="(a, ai) in list" :key="ai" :label="a.title" :name="a.title">
<div class="group" v-for="(b, bi) in a.children" :key="bi">
<p class="label"># {{ b.label }}</p>
<el-row :gutter="10">
<el-col
v-for="(c, ci) in b.children"
:key="ci"
:xs="24"
:sm="12"
:md="8"
:lg="6"
>
<component :is="c" />
</el-col>
</el-row>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script lang="ts" setup name="demo-crud">
import { ref, onActivated } from "vue";
import CrudBase from "./components/crud/base.vue";
import CrudAll from "./components/crud/all.vue";
import CrudDict from "./components/crud/dict.vue";
import CrudEvent from "./components/crud/event.vue";
import CrudService from "./components/crud/service.vue";
import FormOpen from "./components/form/open.vue";
import FormConfig from "./components/form/config.vue";
import FormRequired from "./components/form/required.vue";
import FormLayout from "./components/form/layout.vue";
import FormOptions from "./components/form/options.vue";
import FormHidden from "./components/form/hidden.vue";
import FormDisabled from "./components/form/disabled.vue";
import FormEvent from "./components/form/event.vue";
import FormGroup from "./components/form/group.vue";
import FormChildren from "./components/form/children.vue";
import FormCrud from "./components/form/crud.vue";
import FormRules from "./components/form/rules.vue";
import FormComponent from "./components/form/component/index.vue";
import FormPlugin from "./components/form/plugin/index.vue";
import TableBase from "./components/table/base.vue";
import TableOp from "./components/table/op.vue";
import TableSearch from "./components/table/search.vue";
import TableSelection from "./components/table/selection.vue";
import TableSlot from "./components/table/slot.vue";
import TableSummary from "./components/table/summary.vue";
import TableHidden from "./components/table/hidden.vue";
import TableChildren from "./components/table/children.vue";
import TableContextMenu from "./components/table/context-menu.vue";
import TableDict from "./components/table/dict.vue";
import TableSpanMethod from "./components/table/span-method.vue";
import TableColumnCustom from "./components/table/column-custom.vue";
import TableComponent from "./components/table/component/index.vue";
import UpsertBase from "./components/upsert/base.vue";
import UpsertEvent from "./components/upsert/event.vue";
import UpsertMode from "./components/upsert/mode.vue";
import UpsertHook from "./components/upsert/hook/index.vue";
import SearchBase from "./components/search/base.vue";
import SearchCustom from "./components/search/custom.vue";
import AdvSearchBase from "./components/adv-search/base.vue";
import AdvSearchCustom from "./components/adv-search/custom.vue";
import OtherTsx from "./components/other/tsx";
import OtherTips from "./components/other/tips.vue";
import { useCool } from "/@/cool";
const { route, router } = useCool();
const active = ref();
const list = [
{
title: "cl-crud",
children: [
{
label: "基础",
children: [CrudBase, CrudService, CrudDict, CrudEvent]
},
{
label: "高级",
children: [CrudAll]
}
]
},
{
title: "cl-table",
children: [
{
label: "基础",
children: [
TableBase,
TableOp,
TableSearch,
TableSelection,
TableSlot,
TableDict,
TableHidden,
TableContextMenu,
TableSummary,
TableSpanMethod,
TableChildren
]
},
{
label: "高级",
children: [TableColumnCustom, TableComponent]
}
]
},
{
title: "cl-upsert",
children: [
{
label: "基础",
children: [UpsertBase, UpsertEvent, UpsertMode]
},
{
label: "高级",
children: [UpsertHook]
}
]
},
{
title: "cl-form",
children: [
{
label: "基础",
children: [
FormOpen,
FormConfig,
FormRequired,
FormLayout,
FormOptions,
FormHidden,
FormDisabled,
FormEvent,
FormGroup,
FormChildren,
FormCrud
]
},
{
label: "高级",
children: [FormRules, FormComponent, FormPlugin]
}
]
},
{
title: "cl-search",
children: [
{
label: "基础",
children: [SearchBase, SearchCustom]
}
]
},
{
title: "cl-adv-search",
children: [
{
label: "基础",
children: [AdvSearchBase, AdvSearchCustom]
}
]
},
{
title: "other",
children: [
{
label: "高级",
children: [OtherTsx, OtherTips]
}
]
}
];
function onTabChange(val: any) {
router.replace({
query: {
key: val
}
});
}
onActivated(() => {
const { key } = route.query;
active.value = (key || "cl-crud") as string;
});
</script>
<style lang="scss" scoped>
.crud-demo {
overflow-x: hidden;
background-color: var(--el-bg-color);
padding: 10px;
height: 100%;
box-sizing: border-box;
:deep(.scope) {
border-radius: 8px;
margin-bottom: 10px;
white-space: nowrap;
border: 1px solid var(--el-border-color-light);
cursor: pointer;
transition: all 0.3s;
.h {
display: flex;
align-items: center;
height: 30px;
padding: 10px;
font-size: 12px;
.el-tag {
margin-right: 10px;
}
}
.c {
height: 50px;
padding: 10px;
box-sizing: border-box;
&._svg {
.cl-svg {
margin-right: 15px;
}
}
a {
font-size: 13px;
position: relative;
&:hover {
&:after {
content: "";
width: 100%;
height: 1px;
position: absolute;
bottom: -2px;
left: 0;
background-color: var(--color-primary);
}
}
}
}
.f {
display: flex;
align-items: center;
justify-content: space-between;
height: 30px;
padding: 10px;
font-size: 12px;
.date {
color: #999;
}
}
&:hover {
box-shadow: 0px 0px 10px 1px var(--el-color-info-light-9);
}
}
.group {
margin-bottom: 20px;
.label {
margin-bottom: 10px;
font-size: 15px;
font-weight: bold;
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More