mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2025-12-11 21:12:50 +00:00
发布 7.1.0
This commit is contained in:
parent
70392f0599
commit
93afe3e00b
@ -2,7 +2,7 @@ import { createDir, error, firstUpperCase, readFile, toCamel } from "../utils";
|
||||
import { join } from "path";
|
||||
import { Entity, DistPath } from "./config";
|
||||
import axios from "axios";
|
||||
import { isArray, isEmpty, last, merge, unionBy } from "lodash";
|
||||
import { isArray, isEmpty, last, merge, unionBy } from "lodash-es";
|
||||
import { createWriteStream } from "fs";
|
||||
import prettier from "prettier";
|
||||
import { proxy } from "../../../src/config/proxy";
|
||||
|
||||
23
package.json
23
package.json
@ -9,17 +9,17 @@
|
||||
"lint:eslint": "eslint \"./src/**/*.{vue,ts,tsx}\" --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cool-vue/crud": "^7.1.10",
|
||||
"@cool-vue/crud": "^7.1.11",
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"@vueuse/core": "^10.4.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^1.6.7",
|
||||
"chardet": "^1.6.0",
|
||||
"chardet": "^2.0.0",
|
||||
"core-js": "^3.32.1",
|
||||
"dayjs": "^1.11.10",
|
||||
"echarts": "^5.4.3",
|
||||
"element-plus": "^2.4.3",
|
||||
"element-plus": "^2.5.4",
|
||||
"file-saver": "^2.0.5",
|
||||
"lodash-es": "^4.17.21",
|
||||
"marked": "^11.1.1",
|
||||
@ -31,8 +31,7 @@
|
||||
"pinia": "^2.1.7",
|
||||
"socket.io-client": "^4.7.2",
|
||||
"store": "^2.0.12",
|
||||
"ts-wps": "^1.0.5",
|
||||
"vue": "^3.3.9",
|
||||
"vue": "^3.4.15",
|
||||
"vue-echarts": "^6.6.1",
|
||||
"vue-router": "^4.2.5",
|
||||
"vuedraggable": "^4.1.0",
|
||||
@ -43,26 +42,24 @@
|
||||
"@types/mockjs": "^1.0.7",
|
||||
"@types/node": "^20.5.6",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/prettier": "^3.0.0",
|
||||
"@types/store": "^2.0.2",
|
||||
"@typescript-eslint/eslint-plugin": "^6.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "^6.20.0",
|
||||
"@typescript-eslint/parser": "^6.4.1",
|
||||
"@vitejs/plugin-vue": "^4.3.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.0.2",
|
||||
"@vue/compiler-sfc": "^3.3.4",
|
||||
"@vitejs/plugin-vue": "^5.0.3",
|
||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||
"@vue/compiler-sfc": "^3.4.15",
|
||||
"eslint": "^8.48.0",
|
||||
"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",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.66.1",
|
||||
"terser": "^5.19.2",
|
||||
"terser": "^5.27.0",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.5.0",
|
||||
"vite": "^5.0.12",
|
||||
"vite-plugin-compression": "^0.5.1"
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ Vue.js 是一套用于构建用户界面的渐进式框架。与其它大型框
|
||||
|
||||
<img src='https://cool-js.com/assets/home.1706ac70.png' />
|
||||
|
||||
<span style="font-size: 18px; color: #F56C6C">v6.0.0 新增 Ai 极速编码 ~~~~</span>
|
||||
<span style="font-size: 18px; color: #F56C6C">v7.0.0 新增 Ai 极速编码 ~~~~</span>
|
||||
|
||||
<img src='https://cool-js.com/assets/ai-code2.9a122008.png' />
|
||||
|
||||
|
||||
28
packages/crud/index.d.ts
vendored
28
packages/crud/index.d.ts
vendored
@ -113,6 +113,9 @@ declare namespace Render {
|
||||
// 获取keys
|
||||
type PropKey<T> = keyof RemoveIndex<T> | (string & {});
|
||||
|
||||
// 任意字符串
|
||||
type AnyString = string & {};
|
||||
|
||||
declare namespace ClCrud {
|
||||
interface Label {
|
||||
op: string;
|
||||
@ -291,10 +294,12 @@ declare namespace ClCrud {
|
||||
}
|
||||
|
||||
declare namespace ClTable {
|
||||
type OpButton = Array<"info" | "edit" | "delete" | Render.OpButton>;
|
||||
type OpButton = Array<"info" | "edit" | "delete" | AnyString | Render.OpButton>;
|
||||
|
||||
type ColumnType = "index" | "selection" | "expand" | "op" | AnyString;
|
||||
|
||||
interface Column<T = any> {
|
||||
type: "index" | "selection" | "expand" | "op";
|
||||
type: ColumnType;
|
||||
hidden: boolean | Vue.Ref<boolean>;
|
||||
component: Render.Component;
|
||||
search: {
|
||||
@ -352,10 +357,12 @@ declare namespace ClTable {
|
||||
| "order-asc"
|
||||
>;
|
||||
|
||||
type Plugin = (options: { exposed: Ref }) => void;
|
||||
|
||||
interface Config<T = any> {
|
||||
columns: Column<T>[];
|
||||
autoHeight: boolean;
|
||||
height: string | number;
|
||||
height: any;
|
||||
contextMenu: ContextMenu;
|
||||
defaultSort: {
|
||||
prop: string;
|
||||
@ -364,6 +371,7 @@ declare namespace ClTable {
|
||||
sortRefresh: boolean;
|
||||
emptyText: string;
|
||||
rowKey: string;
|
||||
plugins?: Plugin[];
|
||||
onRowContextmenu?(row: T, column: any, event: any): void;
|
||||
}
|
||||
|
||||
@ -412,7 +420,7 @@ declare namespace ClFormTabs {
|
||||
}
|
||||
|
||||
declare namespace ClForm {
|
||||
type CloseAction = "close" | "save";
|
||||
type CloseAction = "close" | "save" | AnyString;
|
||||
|
||||
interface Rule {
|
||||
type?:
|
||||
@ -456,7 +464,7 @@ declare namespace ClForm {
|
||||
| "splitJoin"
|
||||
| "json"
|
||||
| "empty"
|
||||
| (string & {});
|
||||
| AnyString;
|
||||
|
||||
type HookPipe = HookKey | HookFn;
|
||||
|
||||
@ -512,8 +520,8 @@ declare namespace ClForm {
|
||||
|
||||
interface Config<T = any> {
|
||||
title?: any;
|
||||
height?: string;
|
||||
width?: string;
|
||||
height?: any;
|
||||
width?: any;
|
||||
props: ElementPlus.FormProps;
|
||||
items: Item[];
|
||||
form: obj;
|
||||
@ -535,7 +543,7 @@ declare namespace ClForm {
|
||||
height?: string;
|
||||
width?: string;
|
||||
hideHeader?: boolean;
|
||||
controls?: Array<"fullscreen" | "close">;
|
||||
controls?: Array<"fullscreen" | "close" | AnyString>;
|
||||
[key: string]: any;
|
||||
};
|
||||
[key: string]: any;
|
||||
@ -616,7 +624,7 @@ declare namespace ClUpsert {
|
||||
}
|
||||
|
||||
interface Ref<T = any> extends ClForm.Ref<T> {
|
||||
mode: "add" | "update" | "info";
|
||||
mode: "add" | "update" | "info" | AnyString;
|
||||
}
|
||||
|
||||
interface Options<T = any> extends DeepPartial<Config<T>> {
|
||||
@ -720,6 +728,7 @@ declare interface Config {
|
||||
labelPosition: ElementPlus.FormProps["labelPosition"];
|
||||
labelWidth: ElementPlus.FormProps["labelWidth"];
|
||||
span: number;
|
||||
plugins: ClForm.Plugin[];
|
||||
};
|
||||
table: {
|
||||
stripe: boolean;
|
||||
@ -733,6 +742,7 @@ declare interface Config {
|
||||
align: ElementPlus.Align;
|
||||
headerAlign: ElementPlus.Align;
|
||||
};
|
||||
plugins: ClTable.Plugin[];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@cool-vue/crud",
|
||||
"version": "7.1.10",
|
||||
"version": "7.1.11",
|
||||
"private": false,
|
||||
"main": "./dist/index.umd.min.js",
|
||||
"typings": "types/index.d.ts",
|
||||
@ -13,10 +13,10 @@
|
||||
"dependencies": {
|
||||
"array.prototype.flat": "^1.2.4",
|
||||
"core-js": "^3.21.1",
|
||||
"element-plus": "^2.4.3",
|
||||
"element-plus": "^2.5.4",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"vue": "^3.3.9"
|
||||
"vue": "^3.4.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/array.prototype.flat": "^1.2.1",
|
||||
@ -28,7 +28,7 @@
|
||||
"prettier": "^3.1.0",
|
||||
"sass": "^1.55.0",
|
||||
"sass-loader": "^12.6.0",
|
||||
"typescript": "^4.6.2"
|
||||
"typescript": "^5.3.3"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
|
||||
<cl-flex1 />
|
||||
|
||||
<cl-search-key v-model="v1" @change="onChange" refreshOnInput></cl-search-key>
|
||||
<cl-search-key refreshOnInput></cl-search-key>
|
||||
</cl-row>
|
||||
|
||||
<cl-row>
|
||||
@ -30,7 +30,6 @@
|
||||
<script setup lang="tsx">
|
||||
import { useTable, useForm, useUpsert, useCrud } from "./hooks";
|
||||
import { EditPen } from "@element-plus/icons-vue";
|
||||
import { ref } from "vue";
|
||||
|
||||
interface Data {
|
||||
name?: string;
|
||||
@ -38,12 +37,6 @@ interface Data {
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
const v1 = ref();
|
||||
|
||||
function onChange() {
|
||||
console.log(1111);
|
||||
}
|
||||
|
||||
const Upsert = useUpsert<Data>({
|
||||
items: [
|
||||
{
|
||||
|
||||
@ -64,7 +64,7 @@ export function useAction({
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
console.error(`Prop[${prop}] is not found`);
|
||||
console.error(`[set] ${prop} is not found`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
import { Ref, WatchStopHandle, getCurrentInstance, watch } from "vue";
|
||||
import { useConfig } from "../../../hooks";
|
||||
|
||||
export function usePlugins({ visible }: { visible: Ref<boolean> }) {
|
||||
const that: any = getCurrentInstance();
|
||||
const { style } = useConfig();
|
||||
|
||||
interface Event {
|
||||
onOpen: (() => void)[];
|
||||
onClose: (() => void)[];
|
||||
onSubmit: ((data: obj) => obj)[];
|
||||
onSubmit: ((data: obj) => Promise<obj> | obj)[];
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
@ -21,29 +23,29 @@ export function usePlugins({ visible }: { visible: Ref<boolean> }) {
|
||||
let timer: WatchStopHandle | null = null;
|
||||
|
||||
// 插件创建
|
||||
function create(plugins?: ClForm.Plugin[]) {
|
||||
function create(plugins: ClForm.Plugin[] = []) {
|
||||
for (const i in ev) {
|
||||
ev[i] = [];
|
||||
}
|
||||
|
||||
// 停止监听
|
||||
if (timer) {
|
||||
timer();
|
||||
}
|
||||
|
||||
if (plugins) {
|
||||
plugins.forEach((p) => {
|
||||
p({
|
||||
exposed: that.exposed,
|
||||
onOpen(cb: any) {
|
||||
ev.onOpen.push(cb);
|
||||
},
|
||||
onClose(cb: any) {
|
||||
ev.onClose.push(cb);
|
||||
},
|
||||
onSubmit(cb: any) {
|
||||
ev.onSubmit.push(cb);
|
||||
// 执行
|
||||
[...(style.form.plugins || []), ...plugins].forEach((p) => {
|
||||
const d: any = {
|
||||
exposed: that.exposed
|
||||
};
|
||||
|
||||
for (const i in ev) {
|
||||
d[i] = (cb: any) => {
|
||||
ev[i].push(cb);
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
p(d);
|
||||
});
|
||||
|
||||
timer = watch(
|
||||
@ -62,7 +64,6 @@ export function usePlugins({ visible }: { visible: Ref<boolean> }) {
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 表单提交
|
||||
async function submit(data: any) {
|
||||
|
||||
@ -555,14 +555,15 @@ export default defineComponent({
|
||||
return renderNode(e, {
|
||||
scope: form,
|
||||
slots,
|
||||
custom({ scope }) {
|
||||
custom() {
|
||||
return (
|
||||
<el-button
|
||||
text
|
||||
type={e.type}
|
||||
bg
|
||||
{...e.props}
|
||||
onClick={() => {
|
||||
e.onClick({ scope });
|
||||
e.onClick({ scope: form });
|
||||
}}>
|
||||
{e.label}
|
||||
</el-button>
|
||||
@ -584,6 +585,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
expose({
|
||||
refs,
|
||||
Form,
|
||||
visible,
|
||||
saving,
|
||||
|
||||
21
packages/crud/src/components/table/helper/plugins.ts
Normal file
21
packages/crud/src/components/table/helper/plugins.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { getCurrentInstance } from "vue";
|
||||
import { useConfig } from "../../../hooks";
|
||||
|
||||
export function usePlugins() {
|
||||
const that: any = getCurrentInstance();
|
||||
const { style } = useConfig();
|
||||
|
||||
// 插件创建
|
||||
function create(plugins: ClTable.Plugin[] = []) {
|
||||
// 执行
|
||||
[...(style.table.plugins || []), ...plugins].forEach((p) => {
|
||||
p({
|
||||
exposed: that.exposed
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
create
|
||||
};
|
||||
}
|
||||
@ -10,6 +10,7 @@ import {
|
||||
useTable
|
||||
} from "./helper";
|
||||
import { useCore, useProxy, useElApi, useConfig } from "../../hooks";
|
||||
import { usePlugins } from "./helper/plugins";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-table",
|
||||
@ -54,6 +55,7 @@ export default defineComponent({
|
||||
const { crud } = useCore();
|
||||
const { style } = useConfig();
|
||||
const { Table, config } = useTable(props);
|
||||
const plugin = usePlugins();
|
||||
|
||||
// 排序
|
||||
const Sort = useSort({ config, emit, Table });
|
||||
@ -111,6 +113,7 @@ export default defineComponent({
|
||||
|
||||
useProxy(ctx);
|
||||
expose(ctx);
|
||||
plugin.create(config.plugins);
|
||||
|
||||
return () => {
|
||||
const { renderColumn, renderAppend, renderEmpty } = useRender();
|
||||
|
||||
@ -27,7 +27,7 @@ export function setFocus(prop?: string): ClForm.Plugin {
|
||||
deep(exposed.config.items);
|
||||
|
||||
onOpen(() => {
|
||||
refs[name]?.focus();
|
||||
refs[name]?.focus?.();
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@ -372,6 +372,7 @@
|
||||
display: inline-flex;
|
||||
white-space: nowrap;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
display: inline-flex;
|
||||
@ -479,6 +480,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 6px;
|
||||
padding: 0;
|
||||
|
||||
.el-dialog {
|
||||
&__header {
|
||||
|
||||
@ -89,13 +89,13 @@ function parse(method: "submit" | "bind", { value, hook: pipe, form, prop }: any
|
||||
return false;
|
||||
}
|
||||
|
||||
let pipes = [];
|
||||
let pipes: any[] = [];
|
||||
|
||||
if (isString(pipe)) {
|
||||
if (format[pipe]) {
|
||||
pipes = [pipe];
|
||||
} else {
|
||||
console.error(`Hook[${pipe}] is not found`);
|
||||
console.error(`[hook] ${pipe} is not found`);
|
||||
}
|
||||
} else if (isArray(pipe)) {
|
||||
pipes = pipe;
|
||||
@ -105,13 +105,13 @@ function parse(method: "submit" | "bind", { value, hook: pipe, form, prop }: any
|
||||
} else if (isFunction(pipe)) {
|
||||
pipes = [pipe];
|
||||
} else {
|
||||
console.error(`Hook error`);
|
||||
console.error(`[hook] ${pipe} format error`);
|
||||
}
|
||||
|
||||
let v = value;
|
||||
|
||||
pipes.forEach((e: any) => {
|
||||
let f = null;
|
||||
pipes.forEach((e) => {
|
||||
let f: any = null;
|
||||
|
||||
if (isString(e)) {
|
||||
f = format[e];
|
||||
|
||||
@ -59,7 +59,7 @@ export function dataset(obj: any, key: string, value: any): any {
|
||||
|
||||
return obj;
|
||||
} catch (e) {
|
||||
console.error("Format error", `${key}`);
|
||||
console.error("[dataset] format error", `${key}`);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
@ -39,7 +39,7 @@ export function parseTableDict(value: any, item: ClTable.Column) {
|
||||
}
|
||||
|
||||
// 绑定值
|
||||
let values = [];
|
||||
let values: any[] = [];
|
||||
|
||||
// 格式化值
|
||||
if (isArray(value)) {
|
||||
|
||||
@ -39,7 +39,7 @@ export function parseNode(vnode: any, options: Options): VNode {
|
||||
let comp: VNode | null = null;
|
||||
|
||||
// 插槽模式渲染
|
||||
if (vnode.name.includes("slot-")) {
|
||||
if (vnode.name?.includes("slot-")) {
|
||||
const rn = slots[vnode.name];
|
||||
|
||||
if (rn) {
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
"target": "esnext",
|
||||
"module": "esnext",
|
||||
"strict": true,
|
||||
"noImplicitAny": false,
|
||||
"jsx": "preserve",
|
||||
"importHelpers": true,
|
||||
"moduleResolution": "node",
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
declare const _default: import("vue").DefineComponent<{}, () => false | JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
declare const _default: import("vue").DefineComponent<{}, () => any, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
2
packages/crud/types/components/adv/btn.d.ts
vendored
2
packages/crud/types/components/adv/btn.d.ts
vendored
@ -1,2 +1,2 @@
|
||||
declare const _default: import("vue").DefineComponent<{}, () => JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
declare const _default: import("vue").DefineComponent<{}, () => any, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -15,7 +15,7 @@ declare const _default: import("vue").DefineComponent<{
|
||||
default: () => string[];
|
||||
};
|
||||
onSearch: FunctionConstructor;
|
||||
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("clear" | "reset")[], "clear" | "reset", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("clear" | "reset")[], "clear" | "reset", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
items: {
|
||||
type: PropType<ClForm.Item<any>[]>;
|
||||
default: () => never[];
|
||||
|
||||
@ -9,7 +9,7 @@ declare const ClContextMenu: import("vue").DefineComponent<{
|
||||
type: ObjectConstructor;
|
||||
default: () => {};
|
||||
};
|
||||
}, () => false | JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
show: BooleanConstructor;
|
||||
options: {
|
||||
type: ObjectConstructor;
|
||||
|
||||
@ -5,7 +5,7 @@ declare const _default: import("vue").DefineComponent<{
|
||||
type: StringConstructor;
|
||||
default: string;
|
||||
};
|
||||
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
name: StringConstructor;
|
||||
border: BooleanConstructor;
|
||||
padding: {
|
||||
|
||||
@ -31,7 +31,7 @@ declare const _default: import("vue").DefineComponent<{
|
||||
};
|
||||
}, () => 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<{
|
||||
}>, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("update:modelValue" | "fullscreen-change")[], "update:modelValue" | "fullscreen-change", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
modelValue: {
|
||||
type: BooleanConstructor;
|
||||
default: boolean;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
declare const _default: import("vue").DefineComponent<{
|
||||
title: StringConstructor;
|
||||
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
title: StringConstructor;
|
||||
}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
declare const _default: import("vue").DefineComponent<{
|
||||
label: StringConstructor;
|
||||
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
label: StringConstructor;
|
||||
}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
declare const _default: import("vue").DefineComponent<{}, () => JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
declare const _default: import("vue").DefineComponent<{}, () => any, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -8,7 +8,7 @@ declare const _default: import("vue").DefineComponent<{
|
||||
type: BooleanConstructor;
|
||||
default: boolean;
|
||||
};
|
||||
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
label: StringConstructor;
|
||||
expand: {
|
||||
type: BooleanConstructor;
|
||||
|
||||
@ -6,21 +6,21 @@ declare const _default: import("vue").DefineComponent<{
|
||||
default: () => never[];
|
||||
};
|
||||
justify: {
|
||||
type: PropType<"center" | "justify" | "left" | "right" | "end" | "start" | "match-parent">;
|
||||
type: PropType<"center" | "justify" | "left" | "right" | "start" | "end" | "match-parent">;
|
||||
default: string;
|
||||
};
|
||||
type: {
|
||||
type: PropType<"default" | "card">;
|
||||
default: string;
|
||||
};
|
||||
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("change" | "update:modelValue")[], "change" | "update:modelValue", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("change" | "update:modelValue")[], "change" | "update:modelValue", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
modelValue: (NumberConstructor | StringConstructor)[];
|
||||
labels: {
|
||||
type: ArrayConstructor;
|
||||
default: () => never[];
|
||||
};
|
||||
justify: {
|
||||
type: PropType<"center" | "justify" | "left" | "right" | "end" | "start" | "match-parent">;
|
||||
type: PropType<"center" | "justify" | "left" | "right" | "start" | "end" | "match-parent">;
|
||||
default: string;
|
||||
};
|
||||
type: {
|
||||
@ -33,6 +33,6 @@ declare const _default: import("vue").DefineComponent<{
|
||||
}, {
|
||||
type: "default" | "card";
|
||||
labels: unknown[];
|
||||
justify: "center" | "justify" | "left" | "right" | "end" | "start" | "match-parent";
|
||||
justify: "center" | "justify" | "left" | "right" | "start" | "end" | "match-parent";
|
||||
}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -4,8 +4,8 @@ export declare function useForm(): {
|
||||
config: {
|
||||
[x: string]: any;
|
||||
title?: any;
|
||||
height?: string | undefined;
|
||||
width?: string | undefined;
|
||||
height?: any;
|
||||
width?: any;
|
||||
props: {
|
||||
[x: string]: any;
|
||||
inline?: boolean | undefined;
|
||||
@ -215,7 +215,7 @@ export declare function useForm(): {
|
||||
height?: string | undefined;
|
||||
width?: string | undefined;
|
||||
hideHeader?: boolean | undefined;
|
||||
controls?: ("close" | "fullscreen")[] | undefined;
|
||||
controls?: ("close" | AnyString | "fullscreen")[] | undefined;
|
||||
};
|
||||
};
|
||||
form: obj;
|
||||
|
||||
@ -1,9 +1,7 @@
|
||||
declare const _default: import("vue").DefineComponent<{
|
||||
inner: BooleanConstructor;
|
||||
inline: BooleanConstructor;
|
||||
}, () => false | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
inner: BooleanConstructor;
|
||||
inline: BooleanConstructor;
|
||||
}>>, {
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
declare const _default: import("vue").DefineComponent<{}, () => false | JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
declare const _default: import("vue").DefineComponent<{}, () => any, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
declare const _default: import("vue").DefineComponent<{}, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
}>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
declare const _default: import("vue").DefineComponent<{}, () => JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
declare const _default: import("vue").DefineComponent<{}, () => any, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
declare const _default: import("vue").DefineComponent<{}, () => JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
declare const _default: import("vue").DefineComponent<{}, () => any, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{}>>, {}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { PropType } from "vue";
|
||||
import { type PropType } from "vue";
|
||||
declare const _default: import("vue").DefineComponent<{
|
||||
modelValue: StringConstructor;
|
||||
field: {
|
||||
@ -18,7 +18,8 @@ declare const _default: import("vue").DefineComponent<{
|
||||
type: (NumberConstructor | StringConstructor)[];
|
||||
default: number;
|
||||
};
|
||||
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("change" | "update:modelValue" | "field-change")[], "change" | "update:modelValue" | "field-change", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
refreshOnInput: BooleanConstructor;
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("change" | "update:modelValue" | "field-change")[], "change" | "update:modelValue" | "field-change", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
modelValue: StringConstructor;
|
||||
field: {
|
||||
type: StringConstructor;
|
||||
@ -37,6 +38,7 @@ declare const _default: import("vue").DefineComponent<{
|
||||
type: (NumberConstructor | StringConstructor)[];
|
||||
default: number;
|
||||
};
|
||||
refreshOnInput: BooleanConstructor;
|
||||
}>> & {
|
||||
onChange?: ((...args: any[]) => any) | undefined;
|
||||
"onUpdate:modelValue"?: ((...args: any[]) => any) | undefined;
|
||||
@ -48,5 +50,6 @@ declare const _default: import("vue").DefineComponent<{
|
||||
label: string;
|
||||
value: string;
|
||||
}[];
|
||||
refreshOnInput: boolean;
|
||||
}, {}>;
|
||||
export default _default;
|
||||
|
||||
@ -15,7 +15,7 @@ declare const _default: import("vue").DefineComponent<{
|
||||
};
|
||||
onLoad: FunctionConstructor;
|
||||
onSearch: FunctionConstructor;
|
||||
}, () => true | JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "reset"[], "reset", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, "reset"[], "reset", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
data: {
|
||||
type: ObjectConstructor;
|
||||
default: () => {};
|
||||
|
||||
@ -4,7 +4,7 @@ export declare function useTable(props: any): {
|
||||
config: {
|
||||
columns: {
|
||||
[x: string]: any;
|
||||
type: "op" | "expand" | "selection" | "index";
|
||||
type: ClTable.ColumnType;
|
||||
hidden: boolean | {
|
||||
value: boolean;
|
||||
};
|
||||
@ -98,7 +98,7 @@ export declare function useTable(props: any): {
|
||||
dictAllLevels: boolean;
|
||||
buttons: ((options: {
|
||||
scope: any;
|
||||
}) => ClTable.OpButton) | ("info" | "delete" | "edit" | `slot-${string}` | {
|
||||
}) => ClTable.OpButton) | ("info" | "delete" | "edit" | AnyString | `slot-${string}` | {
|
||||
[x: string]: any;
|
||||
label: string;
|
||||
type?: string | undefined;
|
||||
@ -139,7 +139,7 @@ export declare function useTable(props: any): {
|
||||
children: any[];
|
||||
}[];
|
||||
autoHeight: boolean;
|
||||
height: string | number;
|
||||
height: any;
|
||||
contextMenu: ("info" | "update" | "delete" | "edit" | "refresh" | {
|
||||
[x: string]: any;
|
||||
label: string;
|
||||
@ -159,6 +159,7 @@ export declare function useTable(props: any): {
|
||||
sortRefresh: boolean;
|
||||
emptyText: string;
|
||||
rowKey: string;
|
||||
plugins?: ClTable.Plugin[] | undefined;
|
||||
onRowContextmenu?: ((row: any, column: any, event: any) => void) | undefined;
|
||||
};
|
||||
};
|
||||
|
||||
4
packages/crud/types/components/table/helper/plugins.d.ts
vendored
Normal file
4
packages/crud/types/components/table/helper/plugins.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
/// <reference types="../index" />
|
||||
export declare function usePlugins(): {
|
||||
create: (plugins?: ClTable.Plugin[]) => void;
|
||||
};
|
||||
@ -3,6 +3,6 @@ export declare function useRender(): {
|
||||
renderColumn: (columns: ClTable.Column[]) => (import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}> | null)[];
|
||||
renderEmpty: (emptyText: String) => JSX.Element;
|
||||
renderAppend: () => JSX.Element;
|
||||
renderEmpty: (emptyText: String) => any;
|
||||
renderAppend: () => any;
|
||||
};
|
||||
|
||||
@ -24,7 +24,7 @@ declare const _default: import("vue").DefineComponent<{
|
||||
};
|
||||
}, () => false | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
|
||||
[key: string]: any;
|
||||
}>, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("selection-change" | "sort-change")[], "selection-change" | "sort-change", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}>, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("selection-change" | "sort-change")[], "selection-change" | "sort-change", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
columns: {
|
||||
type: ArrayConstructor;
|
||||
default: () => never[];
|
||||
|
||||
@ -13,7 +13,7 @@ declare const _default: import("vue").DefineComponent<{
|
||||
onClosed: FunctionConstructor;
|
||||
onInfo: FunctionConstructor;
|
||||
onSubmit: FunctionConstructor;
|
||||
}, () => JSX.Element, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("opened" | "closed")[], "opened" | "closed", import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
}, () => any, unknown, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("opened" | "closed")[], "opened" | "closed", import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
|
||||
items: {
|
||||
type: ArrayConstructor;
|
||||
default: () => never[];
|
||||
|
||||
2
packages/crud/types/utils/parse.d.ts
vendored
2
packages/crud/types/utils/parse.d.ts
vendored
@ -17,7 +17,7 @@ export declare function parseTableOpButtons(buttons: any[], { scope }: any): any
|
||||
* 解析扩展组件
|
||||
*/
|
||||
export declare function parseExtensionComponent(vnode: any): {
|
||||
children: JSX.Element;
|
||||
children: any;
|
||||
} | {
|
||||
children?: undefined;
|
||||
};
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,7 @@
|
||||
export const proxy = {
|
||||
"/dev/": {
|
||||
target: "http://127.0.0.1:8001",
|
||||
// target: "http://127.0.0.1:8001",
|
||||
target: "https://test-admin.cool-js.cloud",
|
||||
changeOrigin: true,
|
||||
rewrite: (path: string) => path.replace(/^\/dev/, "")
|
||||
},
|
||||
|
||||
@ -4,7 +4,15 @@ import { Router as VueRouter, RouteRecordRaw } from "vue-router";
|
||||
export declare type Merge<A, B> = Omit<A, keyof B> & B;
|
||||
|
||||
export declare interface ModuleConfig {
|
||||
name?: string;
|
||||
label?: string;
|
||||
description?: string;
|
||||
order?: number;
|
||||
version?: string;
|
||||
logo?: string;
|
||||
author?: string;
|
||||
updateTime?: string;
|
||||
demo?: { name: string; component: Component }[] | string;
|
||||
options?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
@ -20,9 +20,9 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="cl-menu-select" setup>
|
||||
import { useForm, useUpsert } from "@cool-vue/crud";
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { computed, ref, useModel } from "vue";
|
||||
import { computed, ref, useModel, onMounted } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { deepTree } from "/@/cool/utils";
|
||||
|
||||
@ -53,25 +53,24 @@ const list = ref<any[]>([]);
|
||||
|
||||
// 树形列表
|
||||
const tree = computed(() => {
|
||||
return deepTree(cloneDeep(list.value)).filter((e) => !e.parentId);
|
||||
// 过滤掉自己和下级的数据
|
||||
const data = list.value.filter(
|
||||
(e) =>
|
||||
e.id != Form.value?.form.id && (props.type === 0 ? e.type == 0 : props.type > e.type!)
|
||||
);
|
||||
|
||||
return deepTree(cloneDeep(data)).filter((e) => !e.parentId);
|
||||
});
|
||||
|
||||
// 刷新列表
|
||||
async function refresh() {
|
||||
return service.base.sys.menu.list().then((res) => {
|
||||
// 过滤掉自己和下级的数据
|
||||
list.value = res.filter(
|
||||
(e) =>
|
||||
e.id != Form.value?.form.id &&
|
||||
(props.type === 0 ? e.type == 0 : props.type > e.type!)
|
||||
);
|
||||
function refresh() {
|
||||
service.base.sys.menu.list().then((res) => {
|
||||
list.value = res;
|
||||
});
|
||||
}
|
||||
|
||||
useUpsert({
|
||||
onOpened() {
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@ -51,7 +51,7 @@
|
||||
|
||||
<script lang="ts" name="app-topbar" setup>
|
||||
import { computed, markRaw, onMounted, reactive } from "vue";
|
||||
import { orderBy } from "lodash-es";
|
||||
import { isFunction, orderBy } from "lodash-es";
|
||||
import { useBase } from "/$/base";
|
||||
import { module, useCool } from "/@/cool";
|
||||
import RouteNav from "./route-nav.vue";
|
||||
@ -90,9 +90,11 @@ const toolbar = reactive({
|
||||
this.list = await Promise.all(
|
||||
arr.map(async (e) => {
|
||||
if (e) {
|
||||
const c = await (isFunction(e.component) ? e.component() : e.component);
|
||||
|
||||
return {
|
||||
...e,
|
||||
component: markRaw((await e.component).default)
|
||||
component: markRaw(c.default)
|
||||
};
|
||||
}
|
||||
})
|
||||
|
||||
@ -25,7 +25,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="sys-param" setup>
|
||||
import { setFocus, useCrud, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { Document } from "@element-plus/icons-vue";
|
||||
import { reactive } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
@ -202,8 +202,6 @@ const Upsert = useUpsert({
|
||||
data_1: undefined,
|
||||
data_2: undefined
|
||||
});
|
||||
},
|
||||
|
||||
plugins: [setFocus()]
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
@ -47,8 +47,8 @@
|
||||
</cl-crud>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="sys-role" setup>
|
||||
import { useTable, useUpsert, useCrud, setFocus } from "@cool-vue/crud";
|
||||
<script lang="ts" setup name="sys-role">
|
||||
import { useTable, useUpsert, useCrud } from "@cool-vue/crud";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { service } = useCool();
|
||||
@ -117,9 +117,7 @@ const Upsert = useUpsert({
|
||||
...data,
|
||||
departmentIdList: data.departmentIdList || []
|
||||
});
|
||||
},
|
||||
|
||||
plugins: [setFocus()]
|
||||
}
|
||||
});
|
||||
|
||||
// cl-table
|
||||
|
||||
@ -79,7 +79,7 @@ import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useCool } from "/@/cool";
|
||||
import { deepTree, revDeepTree } from "/@/cool/utils";
|
||||
import { isArray } from "lodash-es";
|
||||
import { ContextMenu, setFocus, useForm } from "@cool-vue/crud";
|
||||
import { ContextMenu, useForm } from "@cool-vue/crud";
|
||||
import { Refresh as RefreshIcon, Operation, MoreFilled } from "@element-plus/icons-vue";
|
||||
import { checkPerm } from "/$/base";
|
||||
import { useViewGroup } from "/@/plugins/view";
|
||||
@ -160,8 +160,7 @@ function rowClick(item?: Eps.BaseSysDepartmentEntity) {
|
||||
function rowEdit(item: Eps.BaseSysDepartmentEntity) {
|
||||
const method = item.id ? "update" : "add";
|
||||
|
||||
Form.value?.open(
|
||||
{
|
||||
Form.value?.open({
|
||||
title: "编辑部门",
|
||||
width: "550px",
|
||||
props: {
|
||||
@ -221,9 +220,7 @@ function rowEdit(item: Eps.BaseSysDepartmentEntity) {
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[setFocus()]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 删除部门
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
<cl-dialog
|
||||
v-model="visible"
|
||||
title="聊天窗口"
|
||||
height="700px"
|
||||
height="70vh"
|
||||
width="1200px"
|
||||
padding="0"
|
||||
keep-alive
|
||||
|
||||
@ -36,7 +36,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="cloud-func-info" setup>
|
||||
import { setFocus, useCrud, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { useCool } from "/@/cool";
|
||||
import { Status, CodeSnippets } from "../../dict";
|
||||
import FuncLogs from "../../components/func-logs.vue";
|
||||
@ -89,8 +89,7 @@ const Upsert = useUpsert({
|
||||
...data,
|
||||
content
|
||||
});
|
||||
},
|
||||
plugins: [setFocus("name")]
|
||||
}
|
||||
});
|
||||
|
||||
// cl-table
|
||||
|
||||
@ -1,67 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">cl-context-menu</el-tag>
|
||||
右键菜单
|
||||
</div>
|
||||
<div class="c">
|
||||
<el-button type="success" @contextmenu.stop.prevent="open">右键点击</el-button>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2019/10/23</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ContextMenu } from "@cool-vue/crud";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
function open(e: MouseEvent) {
|
||||
ContextMenu.open(e, {
|
||||
list: [
|
||||
{
|
||||
label: "新增",
|
||||
suffixIcon: "el-icon-plus",
|
||||
callback(done) {
|
||||
ElMessage.info("点击了新增");
|
||||
done();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "编辑",
|
||||
suffixIcon: "el-icon-edit",
|
||||
callback(done) {
|
||||
ElMessage.info("点击了编辑");
|
||||
done();
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "删除",
|
||||
suffixIcon: "el-icon-delete"
|
||||
},
|
||||
{
|
||||
label: "二级",
|
||||
suffixIcon: "el-icon-right",
|
||||
children: [
|
||||
{
|
||||
label: "文本超出隐藏,有一天晚上",
|
||||
ellipsis: true
|
||||
},
|
||||
{
|
||||
label: "禁用",
|
||||
disabled: true
|
||||
},
|
||||
{
|
||||
label: "更多",
|
||||
callback(done) {
|
||||
ElMessage.warning("开发中");
|
||||
done();
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@ -1,27 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">v-copy</el-tag>
|
||||
复制到剪贴板
|
||||
</div>
|
||||
|
||||
<div class="c">
|
||||
<el-button type="success" @click="toCopy"> https://cool-js.com 点击复制</el-button>
|
||||
</div>
|
||||
|
||||
<div class="f">
|
||||
<span class="date">2019/09/25</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
import { ElMessage } from "element-plus";
|
||||
const { copy } = useClipboard();
|
||||
|
||||
function toCopy() {
|
||||
copy("https://cool-js.com");
|
||||
ElMessage.success("保存成功");
|
||||
}
|
||||
</script>
|
||||
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">cl-crud</el-tag>
|
||||
增删改查,超快的
|
||||
</div>
|
||||
<div class="c">
|
||||
<router-link to="/demo/crud">传送门</router-link>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2019/09/25</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">design-page</el-tag>
|
||||
页面设计
|
||||
</div>
|
||||
<div class="c">
|
||||
<router-link to="/design/page">传送门</router-link>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2023/02/01</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">cl-editor</el-tag>
|
||||
编辑器
|
||||
</div>
|
||||
<div class="c">
|
||||
<router-link to="/demo/editor">传送门</router-link>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2019/11/07</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">file</el-tag>
|
||||
文件管理
|
||||
</div>
|
||||
<div class="c">
|
||||
<router-link to="/upload/list">传送门</router-link>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2023/01/01</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">cl-form</el-tag>
|
||||
很强的表单
|
||||
</div>
|
||||
<div class="c">
|
||||
<router-link to="/demo/crud?key=cl-form">传送门</router-link>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2019/01/01</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -1,36 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">cl-svg</el-tag>
|
||||
svg图片库
|
||||
</div>
|
||||
<div class="c _svg">
|
||||
<el-tooltip v-for="(item, index) in list" :key="index" :content="`icon-${item}`">
|
||||
<cl-svg :size="18" :name="`icon-${item}`" />
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2019/09/25</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const list = ref(["like", "video", "rank", "menu", "favor"]);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
._svg {
|
||||
color: #000;
|
||||
|
||||
.cl-svg {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,14 +0,0 @@
|
||||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<el-tag size="small" effect="dark">cl-upload</el-tag>
|
||||
图片上传
|
||||
</div>
|
||||
<div class="c">
|
||||
<router-link to="/demo/upload">传送门</router-link>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2019/09/25</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -120,14 +120,12 @@
|
||||
</template>
|
||||
|
||||
<script lang="tsx" name="demo-crud" setup>
|
||||
import { useCrud, useUpsert, useTable, useAdvSearch, setFocus, useSearch } from "@cool-vue/crud";
|
||||
import { useCrud, useUpsert, useTable, useAdvSearch, useSearch } from "@cool-vue/crud";
|
||||
import { useDict } from "/$/dict";
|
||||
import { reactive, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const v = ref();
|
||||
|
||||
// 基础
|
||||
const { service, refs, setRefs } = useCool();
|
||||
|
||||
@ -315,12 +313,6 @@ const Upsert = useUpsert<Eps.UserInfoEntity>({
|
||||
}
|
||||
],
|
||||
|
||||
// 插件
|
||||
plugins: [
|
||||
// 自动聚焦
|
||||
setFocus("account")
|
||||
],
|
||||
|
||||
// 详情钩子
|
||||
onInfo(data, { next, done }) {
|
||||
// 继续请求 info 接口,可以带其他自定义参数
|
||||
|
||||
@ -50,7 +50,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { useCrud, useForm, useTable, useUpsert, setFocus } from "@cool-vue/crud";
|
||||
import { useCrud, useForm, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { service } = useCool();
|
||||
@ -72,8 +72,7 @@ const Upsert = useUpsert({
|
||||
name: "el-date-picker"
|
||||
}
|
||||
}
|
||||
],
|
||||
plugins: [setFocus()]
|
||||
]
|
||||
});
|
||||
|
||||
// cl-table
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { setFocus, useForm } from "@cool-vue/crud";
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import { setRole } from "./role";
|
||||
|
||||
const Form = useForm();
|
||||
@ -101,9 +101,6 @@ function open(role: string) {
|
||||
}
|
||||
},
|
||||
[
|
||||
// 自动聚焦插件,不传参数则默认第一个item
|
||||
setFocus(),
|
||||
|
||||
// 自定义插件,角色权限控制
|
||||
setRole(role)
|
||||
]
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
import { merge } from "lodash";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
const columns = {
|
||||
UserInfo: {
|
||||
label: "用户信息",
|
||||
minWidth: 200,
|
||||
component: {
|
||||
vm: defineComponent({
|
||||
name: "user-info",
|
||||
|
||||
props: {
|
||||
scope: null
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
return () => {
|
||||
return (
|
||||
<div>
|
||||
<p>{props.scope.name}</p>
|
||||
<p>{props.scope.phone}</p>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} as { [key: string]: DeepPartial<ClTable.Column> };
|
||||
|
||||
/**
|
||||
* 列标签匹配,方便多个列表公用同一个组件
|
||||
* @returns
|
||||
*/
|
||||
export function setColumn(): ClTable.Plugin {
|
||||
return ({ exposed }) => {
|
||||
function deep(arr: ClTable.Column[]) {
|
||||
arr.forEach((e) => {
|
||||
if (e.tag) {
|
||||
merge(e, columns[e.tag]);
|
||||
}
|
||||
deep(e.children || []);
|
||||
});
|
||||
}
|
||||
|
||||
deep(exposed.columns);
|
||||
};
|
||||
}
|
||||
@ -0,0 +1,86 @@
|
||||
<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">预览</el-button>
|
||||
<demo-code :files="['table/plugin/index.vue', 'table/plugin/column.tsx']" />
|
||||
|
||||
<!-- 自定义表格组件 -->
|
||||
<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";
|
||||
import { setColumn } from "./column";
|
||||
|
||||
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"
|
||||
},
|
||||
{
|
||||
tag: "UserInfo"
|
||||
},
|
||||
{
|
||||
label: "工作",
|
||||
prop: "occupation",
|
||||
dict: dict.get("occupation"),
|
||||
minWidth: 140
|
||||
},
|
||||
{
|
||||
label: "创建时间",
|
||||
prop: "createTime",
|
||||
minWidth: 160,
|
||||
sortable: "desc"
|
||||
}
|
||||
],
|
||||
|
||||
//【很重要】配置插件
|
||||
plugins: [setColumn()]
|
||||
});
|
||||
|
||||
const visible = ref(false);
|
||||
|
||||
function open() {
|
||||
visible.value = true;
|
||||
}
|
||||
</script>
|
||||
@ -60,6 +60,7 @@ 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 TablePlugin from "./components/table/plugin/index.vue";
|
||||
|
||||
import UpsertBase from "./components/upsert/base.vue";
|
||||
import UpsertEvent from "./components/upsert/event.vue";
|
||||
@ -115,7 +116,7 @@ const list = [
|
||||
},
|
||||
{
|
||||
label: "高级",
|
||||
children: [TableColumnCustom, TableComponent]
|
||||
children: [TableColumnCustom, TableComponent, TablePlugin]
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@ -1,78 +0,0 @@
|
||||
<template>
|
||||
<div class="demo">
|
||||
<el-row :gutter="10">
|
||||
<el-col v-for="(item, index) in list" :key="index" :xs="24" :sm="12" :md="8" :lg="6">
|
||||
<component :is="item" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="demo">
|
||||
import ContextMenu from "../components/context-menu.vue";
|
||||
import Crud from "../components/crud.vue";
|
||||
import Upload from "../components/upload.vue";
|
||||
import Editor from "../components/editor.vue";
|
||||
import Svg from "../components/svg.vue";
|
||||
import Copy from "../components/copy.vue";
|
||||
import File from "../components/file.vue";
|
||||
import Design from "../components/design.vue";
|
||||
import ClForm from "../components/form.vue";
|
||||
|
||||
const list = [ContextMenu, ClForm, Crud, Upload, Editor, Svg, Copy, File, Design];
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.demo {
|
||||
overflow-x: hidden;
|
||||
|
||||
:deep(.scope) {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 10px;
|
||||
white-space: nowrap;
|
||||
|
||||
.h {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
|
||||
.el-tag {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.c {
|
||||
padding: 10px;
|
||||
height: 50px;
|
||||
box-sizing: border-box;
|
||||
|
||||
a {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
&._svg {
|
||||
.cl-svg {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.f {
|
||||
height: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
font-size: 12px;
|
||||
|
||||
.date {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,40 +0,0 @@
|
||||
<template>
|
||||
<div class="editor">
|
||||
<el-tabs type="card">
|
||||
<el-tab-pane label="Monaco">
|
||||
<cl-editor-monaco v-model="monaco" language="typescript" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="Wang" lazy>
|
||||
<cl-editor-wang v-model="wang" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="demo-editor" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const wang = ref(
|
||||
'<p><span style="font-size: 22px;"><em>富文本编</em></span><span style="color: rgb(216, 68, 147); font-size: 22px;"><em>辑器</em></span></p>'
|
||||
);
|
||||
|
||||
const monaco = ref(`class User {
|
||||
main() {
|
||||
console.log('Name', '神仙都没用')
|
||||
}
|
||||
}
|
||||
|
||||
const user = new User();
|
||||
user.main();
|
||||
`);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.editor {
|
||||
background-color: var(--el-bg-color);
|
||||
padding: 10px;
|
||||
min-height: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
@ -1,144 +0,0 @@
|
||||
<template>
|
||||
<div class="demo">
|
||||
<el-tabs type="card">
|
||||
<el-tab-pane label="普通上传">
|
||||
<cl-upload v-model="v1" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="多图上传" lazy>
|
||||
<cl-upload v-model="v2" text="选择图片" multiple />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="文件上传" lazy>
|
||||
<cl-upload v-model="v3" multiple text="文件上传" type="file" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="拖拽">
|
||||
<div>
|
||||
<el-divider content-position="left"> 拖拽排序 </el-divider>
|
||||
<cl-upload multiple draggable />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-divider content-position="left"> 拖拽上传 </el-divider>
|
||||
<cl-upload drag :size="[160, 300]" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="自定义内容">
|
||||
<cl-upload type="file" multiple draggable custom-class="custom-upload">
|
||||
<el-button :icon="Upload">上传</el-button>
|
||||
|
||||
<template #item="{ item }">
|
||||
<div class="item" v-show="item.url">{{ item.url }}</div>
|
||||
</template>
|
||||
</cl-upload>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="上传校验">
|
||||
<cl-upload :before-upload="onBeforeUpload" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="文件空间">
|
||||
<div>
|
||||
<el-divider content-position="left"> 单选 </el-divider>
|
||||
|
||||
<cl-upload-space v-model="v4" :multiple="false" accept="image/*" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-divider content-position="left"> 多选 </el-divider>
|
||||
|
||||
<cl-upload-space v-model="v5" :limit="3" accept="image/*" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<el-divider content-position="left"> 自定义 </el-divider>
|
||||
|
||||
<cl-upload-space
|
||||
v-model="v6"
|
||||
:multiple="false"
|
||||
:show-btn="false"
|
||||
accept="image/*"
|
||||
:ref="setRefs('uploadSpace')"
|
||||
>
|
||||
<div class="space-custom" @click="refs.uploadSpace?.open">
|
||||
<cl-avatar :size="50" :src="v6" />
|
||||
<p>选择头像</p>
|
||||
</div>
|
||||
</cl-upload-space>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="demo-upload" setup>
|
||||
import { ref } from "vue";
|
||||
import { Upload } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { refs, setRefs } = useCool();
|
||||
|
||||
const v1 = ref("");
|
||||
const v2 = ref([]);
|
||||
const v3 = ref("");
|
||||
const v4 = ref<string[]>([]);
|
||||
const v5 = ref<string[]>([]);
|
||||
const v6 = ref("");
|
||||
|
||||
function onBeforeUpload(file: any) {
|
||||
return new Promise((resolve) => {
|
||||
if (file.size > 100000) {
|
||||
ElMessage.warning("文件不能大于100k");
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.demo {
|
||||
background-color: var(--el-bg-color);
|
||||
padding: 10px;
|
||||
min-height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
:deep(.custom-upload) {
|
||||
.item {
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.space-custom {
|
||||
border: 1px dashed var(--el-border-color);
|
||||
border-radius: 6px;
|
||||
padding: 5px 10px;
|
||||
font-size: 14px;
|
||||
box-sizing: border-box;
|
||||
height: 120px;
|
||||
width: 120px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -57,8 +57,8 @@ export function useAi() {
|
||||
|
||||
// 拼接内容
|
||||
content += msg.content || "";
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
109
src/modules/helper/static/index.scss
Normal file
109
src/modules/helper/static/index.scss
Normal file
@ -0,0 +1,109 @@
|
||||
.plugins {
|
||||
overflow-x: hidden;
|
||||
background-color: var(--el-bg-color);
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.scope {
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
height: 200px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
|
||||
.c {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
padding: 15px;
|
||||
height: calc(100% - 50px);
|
||||
position: relative;
|
||||
|
||||
.set {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
font-size: 18px;
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.det {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
.tag {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.el-tag {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 5px;
|
||||
font-size: 14px;
|
||||
line-height: 1;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
flex: 1;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.author {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.f {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
&.is-add {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--el-disabled-bg-color);
|
||||
border-color: var(--el-disabled-bg-color);
|
||||
width: 180px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 36px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-add):hover {
|
||||
box-shadow: 0px 0px 10px 1px var(--el-color-info-light-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
18
src/modules/helper/static/svg/icon-vue.svg
Normal file
18
src/modules/helper/static/svg/icon-vue.svg
Normal file
@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg
|
||||
t="1706786624724"
|
||||
class="icon"
|
||||
viewBox="0 0 1024 1024"
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
p-id="4208"
|
||||
xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
width="200"
|
||||
height="200"
|
||||
>
|
||||
<path
|
||||
d="M615.6 123.6h165.5L512 589.7 242.9 123.6H63.5L512 900.4l448.5-776.9z"
|
||||
p-id="4209"
|
||||
></path>
|
||||
<path d="M781.1 123.6H615.6L512 303 408.4 123.6H242.9L512 589.7z" p-id="4210"></path>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 536 B |
@ -6,32 +6,38 @@
|
||||
</div>
|
||||
|
||||
<div class="form">
|
||||
<el-form :disabled="temp.disabled">
|
||||
<el-form :disabled="temp.disabled" size="large">
|
||||
<div class="label required">CRUD</div>
|
||||
|
||||
<div class="row">
|
||||
<el-row :gutter="10">
|
||||
<el-col :lg="6" :xs="24" :sm="12">
|
||||
<cl-select
|
||||
class="module"
|
||||
placeholder="请选择模块"
|
||||
size="large"
|
||||
v-model="form.module"
|
||||
:options="module.dirs"
|
||||
label-key="name"
|
||||
value-key="name"
|
||||
allow-create
|
||||
/>
|
||||
</el-col>
|
||||
|
||||
<el-col :lg="6" :xs="24" :sm="12">
|
||||
<el-input
|
||||
class="name"
|
||||
v-model="form.name"
|
||||
placeholder="实体名称,如:收货地址"
|
||||
/>
|
||||
</el-col>
|
||||
|
||||
<el-col :lg="12" :xs="24" :sm="24">
|
||||
<el-input
|
||||
class="columns"
|
||||
size="large"
|
||||
v-model="form.columns"
|
||||
placeholder="请填写字段,如:姓名、年龄、状态"
|
||||
/>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div class="label">其他你想做的事?</div>
|
||||
|
||||
@ -75,7 +81,13 @@
|
||||
<el-button round size="small" @click="copyCode('entity')"
|
||||
>Copy</el-button
|
||||
>
|
||||
<el-button round type="success" size="small" @click="createVue()">
|
||||
<el-button
|
||||
round
|
||||
type="success"
|
||||
size="small"
|
||||
:loading="!codes.vue"
|
||||
@click="createVue()"
|
||||
>
|
||||
生成Vue代码
|
||||
</el-button>
|
||||
</template>
|
||||
@ -161,7 +173,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" name="helper-ai-code" setup>
|
||||
<script lang="tsx" setup name="helper-ai-code">
|
||||
import { onMounted, reactive, watch } from "vue";
|
||||
import { module, useCool, storage } from "/@/cool";
|
||||
import { Promotion, Loading, Close, Check } from "@element-plus/icons-vue";
|
||||
@ -169,10 +181,10 @@ import { ElLoading, ElMessage, ElMessageBox } from "element-plus";
|
||||
import { debounce, isEmpty } from "lodash-es";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
import { useMenu, useAi, useScroll } from "../hooks";
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import Text2 from "../components/text.vue";
|
||||
import type { CodeType } from "../types";
|
||||
import { isDev } from "/@/config";
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import type { CodeType } from "../types";
|
||||
import Text2 from "../components/text.vue";
|
||||
|
||||
const { service, mitt, refs, setRefs } = useCool();
|
||||
const { copy } = useClipboard();
|
||||
@ -184,7 +196,7 @@ const Form = useForm();
|
||||
// 编辑器
|
||||
const editor = reactive({
|
||||
options: {
|
||||
fontSize: 16
|
||||
fontSize: 15
|
||||
}
|
||||
});
|
||||
|
||||
@ -433,7 +445,12 @@ function createFile() {
|
||||
const createVue = debounce((auto?: boolean) => {
|
||||
async function next() {
|
||||
if (codes.entity) {
|
||||
// ai分析
|
||||
await ai.matchType({ columns: temp.data.columns, name: form.name });
|
||||
|
||||
// 生成代码
|
||||
codes.vue = menu.createVue(temp.data);
|
||||
|
||||
stop();
|
||||
|
||||
setTimeout(() => {
|
||||
@ -449,13 +466,35 @@ const createVue = debounce((auto?: boolean) => {
|
||||
ElMessageBox.confirm("此操作将会重新生成vue代码,是否继续?", "提示", {
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
.then(async () => {
|
||||
temp.coding = "vue";
|
||||
codes.vue = "";
|
||||
|
||||
await parseEntity();
|
||||
next();
|
||||
})
|
||||
.catch(() => null);
|
||||
}
|
||||
}, 300);
|
||||
|
||||
// 解析实体
|
||||
async function parseEntity() {
|
||||
await service.base.sys.menu
|
||||
.parse({
|
||||
entity: codes.entity,
|
||||
module: form.module
|
||||
})
|
||||
.then((res) => {
|
||||
temp.data = {
|
||||
...form,
|
||||
...res,
|
||||
router: res.path.replace("/admin", ""),
|
||||
prefix: res.path,
|
||||
api: temp.api
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 监听表单
|
||||
watch(
|
||||
() => form,
|
||||
@ -469,27 +508,16 @@ onMounted(() => {
|
||||
onMessage(content) {
|
||||
codes[temp.coding] = content;
|
||||
},
|
||||
onComplete() {
|
||||
if (temp.coding == "entity") {
|
||||
// 请求 controller
|
||||
service.base.sys.menu
|
||||
.parse({
|
||||
entity: codes.entity,
|
||||
module: form.module
|
||||
})
|
||||
.then((res) => {
|
||||
temp.data = {
|
||||
...form,
|
||||
...res,
|
||||
router: res.path.replace("/admin", ""),
|
||||
prefix: res.path,
|
||||
api: temp.api
|
||||
};
|
||||
|
||||
async onComplete() {
|
||||
switch (temp.coding) {
|
||||
case "entity":
|
||||
await parseEntity();
|
||||
send("controller", temp.data);
|
||||
});
|
||||
} else if (temp.coding == "controller") {
|
||||
break;
|
||||
|
||||
case "controller":
|
||||
createVue(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -501,8 +529,6 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.head {
|
||||
@ -511,6 +537,7 @@ onMounted(() => {
|
||||
|
||||
.container {
|
||||
width: 1040px;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.form {
|
||||
@ -529,33 +556,8 @@ onMounted(() => {
|
||||
}
|
||||
}
|
||||
|
||||
.row {
|
||||
display: flex;
|
||||
margin-bottom: 30px;
|
||||
|
||||
.module {
|
||||
width: 160px;
|
||||
}
|
||||
|
||||
.columns {
|
||||
flex: 1;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.name {
|
||||
width: 200px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.balance {
|
||||
font-size: 15px;
|
||||
margin-left: 10px;
|
||||
white-space: nowrap;
|
||||
|
||||
.el-icon {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
.el-col {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -577,6 +579,7 @@ onMounted(() => {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
flex: 1;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.el-button {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="plugin" @dragover="onDragover" @drop="onDrop">
|
||||
<div class="plugins" @dragover="onDragover" @drop="onDrop">
|
||||
<el-tabs v-model="tab.active" type="card" @tab-change="tab.onChange">
|
||||
<el-tab-pane label="已安装插件" name="installed"> </el-tab-pane>
|
||||
<el-tab-pane label="插件市场" name="shop"> </el-tab-pane>
|
||||
@ -16,11 +16,17 @@
|
||||
<img class="logo" :src="'data:image/jpg;base64,' + item.logo" />
|
||||
|
||||
<div class="det">
|
||||
<div class="title">
|
||||
<div class="tag">
|
||||
<el-tag size="small" effect="dark">{{ item.keyName }}</el-tag>
|
||||
{{ item.name || "-" }} <span>{{ item.version }}</span>
|
||||
<el-tag size="small" effect="dark" type="success"
|
||||
>v{{ item.version }}</el-tag
|
||||
>
|
||||
</div>
|
||||
|
||||
<p class="title">
|
||||
{{ item.name || "未知" }}
|
||||
</p>
|
||||
|
||||
<p class="desc">{{ item.description || "暂无描述" }}</p>
|
||||
|
||||
<div class="author">
|
||||
@ -72,7 +78,7 @@
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="helper-plugin">
|
||||
<script lang="ts" setup name="helper-plugins-serve">
|
||||
import { onActivated, reactive, ref, nextTick } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
@ -272,112 +278,7 @@ onActivated(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.plugin {
|
||||
overflow-x: hidden;
|
||||
background-color: var(--el-bg-color);
|
||||
padding: 10px;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.scope {
|
||||
border-radius: 8px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid var(--el-border-color-light);
|
||||
height: 180px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
|
||||
.c {
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
padding: 15px;
|
||||
height: 130px;
|
||||
position: relative;
|
||||
|
||||
.set {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 10px;
|
||||
font-size: 18px;
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.det {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex: 1;
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
font-size: 14px;
|
||||
.el-tag {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.version {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
flex: 1;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.link {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.author {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.f {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
&.is-add {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--el-disabled-bg-color);
|
||||
border-color: var(--el-disabled-bg-color);
|
||||
width: 180px;
|
||||
|
||||
.el-icon {
|
||||
font-size: 36px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.is-add):hover {
|
||||
box-shadow: 0px 0px 10px 1px var(--el-color-info-light-9);
|
||||
}
|
||||
}
|
||||
}
|
||||
@import "../../static/index.scss";
|
||||
|
||||
.info-header {
|
||||
display: flex;
|
||||
133
src/modules/helper/views/plugins/vue.vue
Normal file
133
src/modules/helper/views/plugins/vue.vue
Normal file
@ -0,0 +1,133 @@
|
||||
<template>
|
||||
<div class="plugins">
|
||||
<el-tabs v-model="tab.active" type="card" @tab-change="tab.onChange">
|
||||
<el-tab-pane label="已安装插件" name="installed"> </el-tab-pane>
|
||||
<el-tab-pane label="插件市场" name="shop"> </el-tab-pane>
|
||||
</el-tabs>
|
||||
|
||||
<el-row :gutter="10">
|
||||
<el-col v-for="(item, index) in list" :key="index" :xs="24" :sm="12" :md="8" :lg="6">
|
||||
<div class="scope">
|
||||
<div class="c">
|
||||
<img class="logo" :src="item.logo" />
|
||||
|
||||
<div class="det">
|
||||
<div class="tag">
|
||||
<el-tag size="small" effect="dark">{{ item.name }}</el-tag>
|
||||
<el-tag size="small" effect="dark" type="success"
|
||||
>v{{ item.version || "1.0.0" }}</el-tag
|
||||
>
|
||||
</div>
|
||||
|
||||
<p class="title">
|
||||
{{ item.label || "未知" }}
|
||||
</p>
|
||||
|
||||
<p class="desc">{{ item.description || "暂无描述" }}</p>
|
||||
|
||||
<div class="author">
|
||||
<span>{{ item.author || "Ta" }}:</span>
|
||||
<span>{{ item.updateTime || "2024-01-01" }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="f">
|
||||
<cl-flex1 />
|
||||
<el-button
|
||||
round
|
||||
@click="det.open(item)"
|
||||
v-if="item.demo && !isEmpty(item.demo)"
|
||||
>示例</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<cl-dialog v-model="det.visible" :title="det.title" width="60%">
|
||||
<el-tabs v-model="det.active" type="card" @tab-change="tab.onChange">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in det.tabs"
|
||||
:key="index"
|
||||
:label="item.name"
|
||||
:name="index"
|
||||
>
|
||||
<component :is="item.component" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</cl-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="helper-plugins-vue">
|
||||
import { reactive, nextTick, markRaw } from "vue";
|
||||
import { module, useCool } from "/@/cool";
|
||||
import { isEmpty, isFunction, isString } from "lodash-es";
|
||||
|
||||
const { router } = useCool();
|
||||
|
||||
// 选项卡
|
||||
const tab = reactive({
|
||||
active: "installed",
|
||||
|
||||
onChange(val: string) {
|
||||
if (val == "shop") {
|
||||
nextTick(() => {
|
||||
tab.active = "installed";
|
||||
window.open("https://cool-js.com/");
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 插件列表
|
||||
const list = module.list
|
||||
.filter((e) => e.type == "plugins")
|
||||
.map((e) => {
|
||||
if (e.author == "COOL") {
|
||||
e.logo = "https://cool-js.com/logo.png";
|
||||
}
|
||||
|
||||
return {
|
||||
...e,
|
||||
children: e.views
|
||||
};
|
||||
});
|
||||
|
||||
// 插件详情
|
||||
const det = reactive({
|
||||
visible: false,
|
||||
title: "",
|
||||
active: 0,
|
||||
tabs: [] as any[],
|
||||
|
||||
async open(item: any) {
|
||||
det.active = 0;
|
||||
|
||||
if (isString(item.demo)) {
|
||||
router.push(item.demo);
|
||||
} else {
|
||||
det.visible = true;
|
||||
det.title = item.label;
|
||||
|
||||
det.tabs = await Promise.all(
|
||||
(item.demo || []).map(async (e: any) => {
|
||||
if (e) {
|
||||
const c = await (isFunction(e.component) ? e.component() : e.component);
|
||||
|
||||
return {
|
||||
...e,
|
||||
component: markRaw(c.default)
|
||||
};
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../static/index.scss";
|
||||
</style>
|
||||
@ -18,6 +18,7 @@
|
||||
width="1070px"
|
||||
padding="0"
|
||||
keep-alive
|
||||
:scrollbar="false"
|
||||
:close-on-click-modal="false"
|
||||
:close-on-press-escape="false"
|
||||
>
|
||||
|
||||
@ -91,7 +91,7 @@
|
||||
import { onActivated, ref } from "vue";
|
||||
import { useBrowser, useCool } from "/@/cool";
|
||||
import { VideoPlay, VideoPause, Plus, Tickets, Delete } from "@element-plus/icons-vue";
|
||||
import { ContextMenu, setFocus, useForm } from "@cool-vue/crud";
|
||||
import { ContextMenu, useForm } from "@cool-vue/crud";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import TaskLogs from "../components/logs.vue";
|
||||
|
||||
@ -179,8 +179,7 @@ async function edit(item?: Eps.TaskInfoEntity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Form.value?.open(
|
||||
{
|
||||
Form.value?.open({
|
||||
title: "编辑计划任务",
|
||||
width: "600px",
|
||||
props: {
|
||||
@ -305,9 +304,7 @@ async function edit(item?: Eps.TaskInfoEntity) {
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
[setFocus()]
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
// 执行一次
|
||||
|
||||
@ -13,6 +13,10 @@
|
||||
<cl-filter label="性别">
|
||||
<cl-select :options="options.gender" prop="gender" :width="120" />
|
||||
</cl-filter>
|
||||
<!-- 状态 -->
|
||||
<cl-filter label="状态">
|
||||
<cl-select :options="options.status" prop="status" :width="120" />
|
||||
</cl-filter>
|
||||
<cl-flex1 />
|
||||
<!-- 关键字搜索 -->
|
||||
<cl-search-key placeholder="搜索昵称、手机号" />
|
||||
@ -78,12 +82,19 @@ const options = reactive({
|
||||
],
|
||||
status: [
|
||||
{
|
||||
label: "启用",
|
||||
value: 1
|
||||
label: "禁用",
|
||||
value: 0,
|
||||
type: "danger"
|
||||
},
|
||||
{
|
||||
label: "禁用",
|
||||
value: 0
|
||||
label: "正常",
|
||||
value: 1,
|
||||
type: "success"
|
||||
},
|
||||
{
|
||||
label: "已注销",
|
||||
value: 2,
|
||||
type: "warning"
|
||||
}
|
||||
]
|
||||
});
|
||||
@ -128,10 +139,8 @@ const Table = useTable({
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
minWidth: 100,
|
||||
component: {
|
||||
name: "cl-switch"
|
||||
}
|
||||
minWidth: 120,
|
||||
dict: options.status
|
||||
},
|
||||
{
|
||||
label: "创建时间",
|
||||
|
||||
@ -1,15 +1,22 @@
|
||||
import { Merge, ModuleConfig } from "/@/cool";
|
||||
|
||||
// npm
|
||||
// import Crud, { locale } from "@cool-vue/crud";
|
||||
// import "@cool-vue/crud/dist/index.css";
|
||||
import Crud, { locale, setFocus } from "@cool-vue/crud";
|
||||
import "@cool-vue/crud/dist/index.css";
|
||||
|
||||
// 调试、自定义crud
|
||||
import Crud, { locale } from "../../../packages/crud/src";
|
||||
import "../../../packages/crud/src/static/index.scss";
|
||||
// import Crud, { locale } from "../../../packages/crud/src";
|
||||
// import "../../../packages/crud/src/static/index.scss";
|
||||
|
||||
export default (): Merge<ModuleConfig, CrudOptions> => {
|
||||
return {
|
||||
label: "CRUD",
|
||||
description: "快速增删改查及一系列辅助组件",
|
||||
author: "COOL",
|
||||
version: "7.1.11",
|
||||
updateTime: "2024-02-01",
|
||||
demo: "/demo/crud",
|
||||
|
||||
// 组件全注册
|
||||
components: Object.values(import.meta.glob("./components/**/*.{vue,tsx}")),
|
||||
|
||||
@ -17,7 +24,16 @@ export default (): Merge<ModuleConfig, CrudOptions> => {
|
||||
options: {
|
||||
style: {
|
||||
table: {
|
||||
// 插件列表
|
||||
plugins: []
|
||||
// contextMenu: [], 是否关闭表格右键菜单
|
||||
},
|
||||
form: {
|
||||
// 插件列表
|
||||
plugins: [
|
||||
// 自动聚焦插件
|
||||
setFocus()
|
||||
]
|
||||
}
|
||||
},
|
||||
dict: {
|
||||
|
||||
@ -16,6 +16,18 @@ registerFormHook("pca", (value, { method, form, prop }) => {
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
label: "省市区选择器",
|
||||
description: "快速增删改查及一系列辅助组件",
|
||||
author: "COOL",
|
||||
version: "1.0.0",
|
||||
updateTime: "2024-02-01",
|
||||
demo: [
|
||||
{
|
||||
name: "基础用法",
|
||||
component: () => import("./demo/base.vue")
|
||||
}
|
||||
],
|
||||
|
||||
components: [
|
||||
// 省市区选择 https://github.com/modood/Administrative-divisions-of-China
|
||||
() => import("./components/index")
|
||||
|
||||
10
src/plugins/distpicker/demo/base.vue
Normal file
10
src/plugins/distpicker/demo/base.vue
Normal file
@ -0,0 +1,10 @@
|
||||
<template>
|
||||
<cl-distpicker v-model="value" />
|
||||
<span :style="{ marginLeft: '20px' }">{{ value }}</span>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const value = ref([]);
|
||||
</script>
|
||||
@ -26,7 +26,7 @@ const props = defineProps({
|
||||
options: Object,
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 400
|
||||
default: 500
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
@ -86,7 +86,7 @@ function create() {
|
||||
theme: "default",
|
||||
language: props.language,
|
||||
minimap: {
|
||||
enabled: true
|
||||
enabled: false
|
||||
},
|
||||
automaticLayout: true,
|
||||
scrollbar: {
|
||||
@ -2,9 +2,21 @@ import { ModuleConfig } from "/@/cool";
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
label: "代码编辑器",
|
||||
description: "基于 monaco 封装的代码编辑器",
|
||||
author: "COOL",
|
||||
version: "1.0.0",
|
||||
updateTime: "2024-02-01",
|
||||
demo: [
|
||||
{
|
||||
name: "基础用法",
|
||||
component: () => import("./demo/base.vue")
|
||||
}
|
||||
],
|
||||
|
||||
components: [
|
||||
// 代码编辑器 https://www.npmjs.com/package/monaco-editor
|
||||
() => import("./components/index.vue")
|
||||
() => import("./components/monaco.vue")
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
28
src/plugins/editor-monaco/demo/base.vue
Normal file
28
src/plugins/editor-monaco/demo/base.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<cl-editor-monaco v-model="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const value = ref([
|
||||
{
|
||||
label: "A",
|
||||
children: [
|
||||
{
|
||||
label: "B",
|
||||
children: [
|
||||
{
|
||||
label: "C",
|
||||
children: [
|
||||
{
|
||||
label: "D"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]);
|
||||
</script>
|
||||
@ -2,6 +2,18 @@ import { ModuleConfig } from "/@/cool";
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
label: "编辑器内容预览",
|
||||
description: "基于 monaco、wang 等编辑器的内容预览组件",
|
||||
author: "COOL",
|
||||
version: "1.0.0",
|
||||
updateTime: "2024-02-01",
|
||||
demo: [
|
||||
{
|
||||
name: "基础用法",
|
||||
component: () => import("./demo/base.vue")
|
||||
}
|
||||
],
|
||||
|
||||
components: [() => import("./components/preview.vue")]
|
||||
};
|
||||
};
|
||||
|
||||
29
src/plugins/editor-preview/demo/base.vue
Normal file
29
src/plugins/editor-preview/demo/base.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<cl-editor-preview v-model="wang" name="wang" text="查看内容" />
|
||||
<cl-editor-preview
|
||||
v-model="monaco"
|
||||
name="monaco"
|
||||
:props="{
|
||||
language: 'typescript'
|
||||
}"
|
||||
text="查看代码"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const wang = ref(
|
||||
'<p><span style="font-size: 22px;"><em>富文本编</em></span><span style="color: rgb(216, 68, 147); font-size: 22px;"><em>辑器</em></span></p>'
|
||||
);
|
||||
|
||||
const monaco = ref(`class User {
|
||||
main() {
|
||||
console.log('Name', 'COOL')
|
||||
}
|
||||
}
|
||||
|
||||
const user = new User();
|
||||
user.main();
|
||||
`);
|
||||
</script>
|
||||
@ -68,7 +68,7 @@ export default defineComponent({
|
||||
// 高度
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 400
|
||||
default: 500
|
||||
},
|
||||
// 禁用
|
||||
disabled: Boolean,
|
||||
|
||||
@ -2,6 +2,18 @@ import { ModuleConfig } from "/@/cool";
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
label: "富文本编辑器",
|
||||
description: "基于 wangEditor 封装的富文本编辑器",
|
||||
author: "COOL",
|
||||
version: "1.0.0",
|
||||
updateTime: "2024-02-01",
|
||||
demo: [
|
||||
{
|
||||
name: "基础用法",
|
||||
component: () => import("./demo/base.vue")
|
||||
}
|
||||
],
|
||||
|
||||
components: [() => import("./components/wang.vue")]
|
||||
};
|
||||
};
|
||||
|
||||
11
src/plugins/editor-wang/demo/base.vue
Normal file
11
src/plugins/editor-wang/demo/base.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<cl-editor-wang v-model="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const value = ref(
|
||||
'<p><span style="font-size: 22px;"><em>富文本编</em></span><span style="color: rgb(216, 68, 147); font-size: 22px;"><em>辑器</em></span></p>'
|
||||
);
|
||||
</script>
|
||||
@ -141,7 +141,7 @@ export default defineComponent({
|
||||
|
||||
function open() {
|
||||
if (!props.columns) {
|
||||
return console.error("columns is required");
|
||||
return console.error("<cl-export-btn /> columns is required");
|
||||
}
|
||||
|
||||
// 表格列
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
|
||||
<cl-form ref="Form">
|
||||
<template #slot-upload>
|
||||
<div class="upload">
|
||||
<div class="upload" v-if="!upload.filename">
|
||||
<div class="tips">
|
||||
<span>{{ tips }}</span>
|
||||
<el-button type="primary" text bg @click="download">下载模版</el-button>
|
||||
@ -21,9 +21,95 @@
|
||||
:size="[220, '100%']"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="progress" v-if="progress > 0">
|
||||
<el-progress type="dashboard" :percentage="progress" />
|
||||
<template #slot-list>
|
||||
<div class="data-table" v-if="list.length">
|
||||
<div class="head">
|
||||
<el-button type="success" @click="clear">重新上传</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="table.selection.length == 0"
|
||||
@click="table.del()"
|
||||
>批量删除</el-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<div class="cl-table">
|
||||
<el-table
|
||||
border
|
||||
small
|
||||
:data="list"
|
||||
max-height="600px"
|
||||
@selection-change="table.onSelectionChange"
|
||||
@row-click="
|
||||
(row) => {
|
||||
row._edit = true;
|
||||
}
|
||||
"
|
||||
>
|
||||
<el-table-column
|
||||
type="selection"
|
||||
width="60px"
|
||||
align="center"
|
||||
fixed="left"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="序号"
|
||||
type="index"
|
||||
width="80px"
|
||||
align="center"
|
||||
fixed="left"
|
||||
:index="table.onIndex"
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
v-for="item in table.header"
|
||||
:key="item"
|
||||
:prop="item"
|
||||
:label="item"
|
||||
min-width="160px"
|
||||
align="center"
|
||||
>
|
||||
<template #default="scope">
|
||||
<span v-if="!scope.row._edit">{{ scope.row[item] }}</span>
|
||||
|
||||
<template v-else>
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="scope.row[item]"
|
||||
clearable
|
||||
:placeholder="item"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="100px" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
text
|
||||
bg
|
||||
type="danger"
|
||||
@click.stop="table.del(scope.$index)"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
<div class="pagination">
|
||||
<el-pagination
|
||||
background
|
||||
layout="total, prev, pager, next"
|
||||
:total="upload.list.length"
|
||||
:page-size="pagination.size"
|
||||
v-model:current-page="pagination.page"
|
||||
@current-change="pagination.onCurrentChange"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -33,7 +119,7 @@
|
||||
<script lang="ts" setup name="cl-import-btn">
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { reactive, ref, type PropType } from "vue";
|
||||
import { reactive, type PropType, computed } from "vue";
|
||||
import * as XLSX from "xlsx";
|
||||
import chardet from "chardet";
|
||||
import { extname } from "/@/cool/utils";
|
||||
@ -75,17 +161,65 @@ const Form = useForm();
|
||||
// 上传信息
|
||||
const upload = reactive({
|
||||
filename: "",
|
||||
data: [] as any[]
|
||||
file: null as File | null,
|
||||
list: [] as any[]
|
||||
});
|
||||
|
||||
// 进度
|
||||
const progress = ref(0);
|
||||
// 分页信息
|
||||
const pagination = reactive({
|
||||
size: 20,
|
||||
page: 1,
|
||||
onCurrentChange(page: number) {
|
||||
pagination.page = page;
|
||||
}
|
||||
});
|
||||
|
||||
// 数据表格
|
||||
const table = reactive({
|
||||
// 表头
|
||||
header: [] as string[],
|
||||
|
||||
// 选中列表
|
||||
selection: [] as any[],
|
||||
|
||||
// 删除行
|
||||
del(index?: number) {
|
||||
if (index !== undefined) {
|
||||
upload.list.splice(index, 1);
|
||||
} else {
|
||||
upload.list = upload.list.filter((a) => {
|
||||
return !table.selection.includes(a._index);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
// 序号
|
||||
onIndex(index: number) {
|
||||
return index + 1 + (pagination.page - 1) * pagination.size;
|
||||
},
|
||||
|
||||
// 选中
|
||||
onSelectionChange(arr: any[]) {
|
||||
table.selection = arr.map((e) => e._index);
|
||||
}
|
||||
});
|
||||
|
||||
// 数据列表
|
||||
const list = computed(() => {
|
||||
return upload.list.filter((_, i) => {
|
||||
return (
|
||||
i >= (pagination.page - 1) * pagination.size && i < pagination.page * pagination.size
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
// 清空
|
||||
function clear() {
|
||||
progress.value = 0;
|
||||
upload.filename = "";
|
||||
upload.data = [];
|
||||
upload.file = null;
|
||||
upload.list = [];
|
||||
table.header = [];
|
||||
table.selection = [];
|
||||
}
|
||||
|
||||
// 打开
|
||||
@ -94,49 +228,46 @@ function open() {
|
||||
|
||||
Form.value?.open({
|
||||
title: "导入",
|
||||
width: "800px",
|
||||
width: computed(() => (upload.filename ? "80%" : "800px")),
|
||||
dialog: {
|
||||
"close-on-press-escape": false
|
||||
},
|
||||
items: [
|
||||
...(props.onConfig ? props.onConfig(Form) : []),
|
||||
{
|
||||
label: "",
|
||||
prop: "file",
|
||||
props: {
|
||||
labelWidth: "0"
|
||||
},
|
||||
component: {
|
||||
name: "slot-upload"
|
||||
}
|
||||
},
|
||||
{
|
||||
component: {
|
||||
name: "slot-list"
|
||||
}
|
||||
}
|
||||
],
|
||||
op: {
|
||||
saveButtonText: "提交"
|
||||
},
|
||||
on: {
|
||||
submit(data, { done, close }) {
|
||||
submit(_, { done, close }) {
|
||||
if (!upload.filename) {
|
||||
done();
|
||||
return ElMessage.error("请选择文件");
|
||||
}
|
||||
|
||||
if (props.onSubmit) {
|
||||
props.onSubmit(
|
||||
{
|
||||
...data,
|
||||
list: upload.data
|
||||
},
|
||||
{ done, close, setProgress }
|
||||
);
|
||||
props.onSubmit(upload, { done, close });
|
||||
} else {
|
||||
console.error("cl-import-btn 未配置 onSubmit");
|
||||
ElMessage.error("<cl-import-btn /> 未配置 onSubmit 参数");
|
||||
done();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 上传
|
||||
function onUpload(raw: File, _: any, { next }: any) {
|
||||
const reader = new FileReader();
|
||||
const ext = extname(raw.name);
|
||||
@ -161,8 +292,26 @@ function onUpload(raw: File, _: any, { next }: any) {
|
||||
}
|
||||
}
|
||||
|
||||
upload.data = json;
|
||||
upload.list = json.map((e, i) => {
|
||||
return {
|
||||
...e,
|
||||
_index: i
|
||||
};
|
||||
});
|
||||
upload.filename = raw.name;
|
||||
upload.file = raw;
|
||||
|
||||
const sheet = workbook.Sheets[Object.keys(workbook.Sheets)[0]];
|
||||
|
||||
for (let i in sheet) {
|
||||
if (i[0] === "!") continue;
|
||||
|
||||
const row = i.match(/[0-9]+/)?.[0];
|
||||
|
||||
if (row == "1") {
|
||||
table.header.push(sheet[i].v);
|
||||
}
|
||||
}
|
||||
|
||||
emit("change", json);
|
||||
};
|
||||
@ -186,15 +335,9 @@ function download() {
|
||||
link.click();
|
||||
}
|
||||
|
||||
// 设置进度值
|
||||
function setProgress(val: string) {
|
||||
progress.value = parseInt(val);
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
clear,
|
||||
setProgress,
|
||||
Form
|
||||
});
|
||||
</script>
|
||||
@ -229,9 +372,15 @@ defineExpose({
|
||||
}
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-top: 20px;
|
||||
.data-table {
|
||||
.head {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -2,6 +2,18 @@ import { ModuleConfig } from "/@/cool";
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
label: "Excel",
|
||||
description: "表格的导入、导出组件",
|
||||
author: "COOL",
|
||||
version: "1.0.0",
|
||||
updateTime: "2024-02-01",
|
||||
demo: [
|
||||
{
|
||||
name: "基础用法",
|
||||
component: () => import("./demo/base.vue")
|
||||
}
|
||||
],
|
||||
|
||||
components: [
|
||||
() => import("./components/import-btn.vue"),
|
||||
() => import("./components/export-btn")
|
||||
|
||||
80
src/plugins/excel/demo/base.vue
Normal file
80
src/plugins/excel/demo/base.vue
Normal file
@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<cl-crud ref="Crud">
|
||||
<cl-row>
|
||||
<!-- 刷新按钮 -->
|
||||
<cl-refresh-btn />
|
||||
|
||||
<cl-flex1 />
|
||||
|
||||
<!-- 导入 -->
|
||||
<cl-import-btn template="/用户导入模版.xlsx" :on-submit="onImpSubmit" />
|
||||
|
||||
<!-- 导出 -->
|
||||
<cl-export-btn :columns="Table?.columns" />
|
||||
</cl-row>
|
||||
|
||||
<cl-row>
|
||||
<!-- 表格 -->
|
||||
<cl-table ref="Table" :auto-height="false" />
|
||||
</cl-row>
|
||||
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
|
||||
<!-- 分页 -->
|
||||
<cl-pagination />
|
||||
</cl-row>
|
||||
</cl-crud>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup>
|
||||
import { useCrud, useTable } from "@cool-vue/crud";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useDict } from "/$/dict";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const { service } = useCool();
|
||||
const { dict } = useDict();
|
||||
|
||||
// crud
|
||||
const Crud = useCrud(
|
||||
{
|
||||
service: service.test
|
||||
},
|
||||
(app) => {
|
||||
app.refresh({ size: 10 });
|
||||
}
|
||||
);
|
||||
|
||||
// 表格
|
||||
const Table = useTable({
|
||||
columns: [
|
||||
{
|
||||
label: "姓名",
|
||||
prop: "name"
|
||||
},
|
||||
{
|
||||
label: "手机号",
|
||||
prop: "phone"
|
||||
},
|
||||
{
|
||||
label: "账号",
|
||||
prop: "account"
|
||||
},
|
||||
{
|
||||
label: "存款(元)",
|
||||
prop: "wages"
|
||||
},
|
||||
{
|
||||
label: "工作",
|
||||
prop: "occupation",
|
||||
dict: dict.get("occupation")
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
function onImpSubmit(data: { list: any[]; file: File }, { done, close }: any) {
|
||||
close();
|
||||
ElMessage.success(`已提交${data.list.length}条数据`);
|
||||
}
|
||||
</script>
|
||||
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="file-viewer">
|
||||
<div class="viewer-image">
|
||||
<!-- 图片 -->
|
||||
<el-image-viewer
|
||||
v-if="img.visible"
|
||||
@ -8,34 +8,38 @@
|
||||
teleported
|
||||
@close="close"
|
||||
/>
|
||||
|
||||
<!-- Wps -->
|
||||
<wps :ref="setRefs('wps')" />
|
||||
</div>
|
||||
|
||||
<!-- 文档 -->
|
||||
<cl-dialog v-model="doc.visible" title="文档预览" height="70vh" width="80%" :scrollbar="false">
|
||||
<div class="viewer-doc" v-loading="doc.loading">
|
||||
<iframe :src="doc.url" :ref="setRefs('docIframe')" />
|
||||
</div>
|
||||
</cl-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="file-viewer">
|
||||
import { reactive, toRaw } from "vue";
|
||||
import { has } from "lodash-es";
|
||||
import { useCool } from "/@/cool";
|
||||
import Wps from "./wps.vue";
|
||||
import { reactive, nextTick } from "vue";
|
||||
import { getType } from "../../utils";
|
||||
import type { Upload } from "../../types";
|
||||
|
||||
enum WpsType {
|
||||
word = "Writer",
|
||||
excel = "Spreadsheet",
|
||||
ppt = "Presentation",
|
||||
pdf = "Pdf"
|
||||
}
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { refs, setRefs } = useCool();
|
||||
|
||||
// 图片预览
|
||||
const img = reactive({
|
||||
visible: false,
|
||||
url: ""
|
||||
});
|
||||
|
||||
// 文档预览
|
||||
const doc = reactive({
|
||||
visible: false,
|
||||
loading: false,
|
||||
url: ""
|
||||
});
|
||||
|
||||
// 打开
|
||||
function open(item: Upload.Item) {
|
||||
if (item?.type) {
|
||||
// 链接
|
||||
@ -44,7 +48,7 @@ function open(item: Upload.Item) {
|
||||
// 类型
|
||||
const type = getType(url);
|
||||
|
||||
// 图片
|
||||
// 图片预览
|
||||
if (type == "image") {
|
||||
img.visible = true;
|
||||
img.url = url;
|
||||
@ -52,16 +56,26 @@ function open(item: Upload.Item) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// WPS
|
||||
if (has(WpsType, type)) {
|
||||
// @ts-ignore
|
||||
return refs.wps?.open(Object.assign(toRaw(item), { officeType: WpsType[item.type] }));
|
||||
// 文档预览
|
||||
if (["word", "excel", "ppt", "pdf"].includes(type)) {
|
||||
doc.visible = true;
|
||||
doc.loading = true;
|
||||
doc.url = `https://view.officeapps.live.com/op/view.aspx?src=${decodeURIComponent(url)}`;
|
||||
|
||||
nextTick(() => {
|
||||
refs.docIframe.onload = () => {
|
||||
doc.loading = false;
|
||||
};
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
window.open(item.url);
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭
|
||||
function close() {
|
||||
img.visible = false;
|
||||
}
|
||||
@ -72,7 +86,18 @@ defineExpose({
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.file-viewer {
|
||||
.viewer-image {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.viewer-doc {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -1,87 +0,0 @@
|
||||
<template>
|
||||
<cl-dialog v-model="visible" width="80%" @closed="onClosed">
|
||||
<div class="file-wps"></div>
|
||||
</cl-dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
/**
|
||||
* wps预览组件
|
||||
* @param {string} officeType Writer | Spreadsheet | Presentation | Pdf
|
||||
* @param {string} fileId 文件 id,用户自定义
|
||||
* @param {string} mount 挂载节点
|
||||
* @param {string} token 授权
|
||||
*/
|
||||
|
||||
import { ElMessage } from "element-plus";
|
||||
import WebOfficeSDK, { IAppConfig } from "ts-wps";
|
||||
import { ref } from "vue";
|
||||
import { useBase } from "/$/base";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { service } = useCool();
|
||||
const { user } = useBase();
|
||||
|
||||
// 应用ID
|
||||
const appId = ref("");
|
||||
|
||||
// 是否可见
|
||||
const visible = ref(false);
|
||||
|
||||
// 获取应用ID
|
||||
async function getAppId() {
|
||||
if (!appId.value) {
|
||||
await service.space.info.getConfig().then((res) => {
|
||||
appId.value = res.appId;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
let wps: any = null;
|
||||
|
||||
/**
|
||||
* 打开wps预览
|
||||
* @param data WebOfficeSDK.IAppConfig
|
||||
*/
|
||||
async function open(data: IAppConfig) {
|
||||
visible.value = true;
|
||||
|
||||
await getAppId();
|
||||
|
||||
if (!appId.value) {
|
||||
return ElMessage.error("请先配置WPS的应用ID");
|
||||
}
|
||||
|
||||
wps = WebOfficeSDK.init({
|
||||
officeType: WebOfficeSDK.OfficeType[data.officeType],
|
||||
appId: appId.value,
|
||||
fileId: data.fileId,
|
||||
mount: document.querySelector(".file-wps") as HTMLElement,
|
||||
fileToken: {
|
||||
token: user.token,
|
||||
timeout: 10 * 60 * 1000
|
||||
}
|
||||
});
|
||||
|
||||
wps.ready();
|
||||
}
|
||||
|
||||
function onClosed() {
|
||||
wps.destroy();
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.file-wps {
|
||||
height: 70vh;
|
||||
|
||||
iframe {
|
||||
height: calc(100% - 5px) !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@ -1,5 +1,42 @@
|
||||
export default () => {
|
||||
return {
|
||||
label: "文件上传",
|
||||
description: "基于 el-upload 封装的文件上传组件",
|
||||
author: "COOL",
|
||||
version: "1.0.0",
|
||||
updateTime: "2024-02-01",
|
||||
demo: [
|
||||
{
|
||||
name: "基础用法",
|
||||
component: () => import("./demo/base.vue")
|
||||
},
|
||||
{
|
||||
name: "多图上传",
|
||||
component: () => import("./demo/multiple.vue")
|
||||
},
|
||||
{
|
||||
name: "文件上传",
|
||||
component: () => import("./demo/file.vue")
|
||||
},
|
||||
{
|
||||
name: "可拖拽",
|
||||
component: () => import("./demo/drag.vue")
|
||||
},
|
||||
{
|
||||
name: "自定义内容",
|
||||
component: () => import("./demo/custom.vue")
|
||||
},
|
||||
{
|
||||
name: "上传校验",
|
||||
component: () => import("./demo/check.vue")
|
||||
},
|
||||
{
|
||||
name: "文件空间",
|
||||
component: () => import("./demo/space.vue")
|
||||
}
|
||||
],
|
||||
|
||||
// 参数
|
||||
options: {
|
||||
// 尺寸
|
||||
size: 120,
|
||||
|
||||
9
src/plugins/upload/demo/base.vue
Normal file
9
src/plugins/upload/demo/base.vue
Normal file
@ -0,0 +1,9 @@
|
||||
<template>
|
||||
<cl-upload v-model="value" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
const value = ref("");
|
||||
</script>
|
||||
17
src/plugins/upload/demo/check.vue
Normal file
17
src/plugins/upload/demo/check.vue
Normal file
@ -0,0 +1,17 @@
|
||||
<template>
|
||||
<cl-upload :before-upload="onBeforeUpload" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
function onBeforeUpload(file: any) {
|
||||
return new Promise((resolve) => {
|
||||
if (file.size > 100000) {
|
||||
ElMessage.warning("文件不能大于100k");
|
||||
} else {
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user