mirror of
https://github.com/cool-team-official/cool-admin-vue.git
synced 2024-11-01 14:10:27 +08:00
优化
This commit is contained in:
parent
664585b21c
commit
091f09ef2b
@ -40,10 +40,10 @@ export function cool(): Plugin {
|
|||||||
code: 1000,
|
code: 1000,
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
}).catch((message: string) => {
|
}).catch((err: Error) => {
|
||||||
done({
|
done({
|
||||||
code: 1001,
|
code: 1001,
|
||||||
message
|
message: err.message
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,33 @@ import { createWriteStream } from "fs";
|
|||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import config from "./config";
|
import config from "./config";
|
||||||
|
|
||||||
|
interface Options {
|
||||||
|
list: {
|
||||||
|
prefix: string;
|
||||||
|
name: string;
|
||||||
|
columns: any[];
|
||||||
|
api: {
|
||||||
|
name: string;
|
||||||
|
method: string;
|
||||||
|
path: string;
|
||||||
|
summary: string;
|
||||||
|
dts: {
|
||||||
|
parameters: {
|
||||||
|
description: string;
|
||||||
|
schema: {
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
name: string;
|
||||||
|
required: boolean;
|
||||||
|
}[];
|
||||||
|
};
|
||||||
|
}[];
|
||||||
|
}[];
|
||||||
|
service: {
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// 临时目录路径
|
// 临时目录路径
|
||||||
const tempPath = join(__dirname, "../../temp");
|
const tempPath = join(__dirname, "../../temp");
|
||||||
|
|
||||||
@ -23,13 +50,13 @@ function getType({ entityName, propertyName, type }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Entity
|
// 创建 Entity
|
||||||
function createEntity({ list }: any) {
|
function createEntity({ list }: Options) {
|
||||||
const t0: any[] = [];
|
const t0: any[] = [];
|
||||||
|
|
||||||
for (const item of list) {
|
for (const item of list) {
|
||||||
if (!item.name) continue;
|
if (!item.name) continue;
|
||||||
const t = [`interface ${item.name} {`];
|
const t = [`interface ${item.name} {`];
|
||||||
for (const col of item.columns) {
|
for (const col of item.columns || []) {
|
||||||
// 描述
|
// 描述
|
||||||
t.push("\n");
|
t.push("\n");
|
||||||
t.push("/**\n");
|
t.push("/**\n");
|
||||||
@ -56,7 +83,7 @@ function createEntity({ list }: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建 Service
|
// 创建 Service
|
||||||
function createService({ list, service }: any) {
|
function createService({ list, service }: Options) {
|
||||||
const t0: any[] = [];
|
const t0: any[] = [];
|
||||||
|
|
||||||
const t1 = [
|
const t1 = [
|
||||||
@ -81,7 +108,7 @@ function createService({ list, service }: any) {
|
|||||||
|
|
||||||
if (d[i].namespace) {
|
if (d[i].namespace) {
|
||||||
// 查找配置
|
// 查找配置
|
||||||
const item = list.find((e: any) => (e.prefix || "").includes(d[i].namespace));
|
const item = list.find((e) => (e.prefix || "").includes(d[i].namespace));
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
const t = [`interface ${name} {`];
|
const t = [`interface ${name} {`];
|
||||||
@ -93,9 +120,9 @@ function createService({ list, service }: any) {
|
|||||||
// 权限列表
|
// 权限列表
|
||||||
const permission: string[] = [];
|
const permission: string[] = [];
|
||||||
|
|
||||||
item.api.forEach((a: any) => {
|
item.api.forEach((a) => {
|
||||||
// 方法名
|
// 方法名
|
||||||
const n = toCamel(a.name || last(a.path.split("/"))).replace(
|
const n = toCamel(a.name || last(a.path.split("/")) || "").replace(
|
||||||
/[:\/-]/g,
|
/[:\/-]/g,
|
||||||
""
|
""
|
||||||
);
|
);
|
||||||
@ -107,7 +134,7 @@ function createService({ list, service }: any) {
|
|||||||
// 参数列表
|
// 参数列表
|
||||||
const { parameters = [] } = a.dts || {};
|
const { parameters = [] } = a.dts || {};
|
||||||
|
|
||||||
parameters.forEach((p: any) => {
|
parameters.forEach((p) => {
|
||||||
if (p.description) {
|
if (p.description) {
|
||||||
q.push(`\n/** ${p.description} */\n`);
|
q.push(`\n/** ${p.description} */\n`);
|
||||||
}
|
}
|
||||||
@ -227,12 +254,12 @@ function createService({ list, service }: any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建描述文件
|
// 创建描述文件
|
||||||
export async function createEps({ list, service }: any) {
|
export async function createEps(options: Options) {
|
||||||
// 文件内容
|
// 文件内容
|
||||||
const text = `
|
const text = `
|
||||||
declare namespace Eps {
|
declare namespace Eps {
|
||||||
${createEntity({ list })}
|
${createEntity(options)}
|
||||||
${createService({ list, service })}
|
${createService(options)}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
@ -261,8 +288,18 @@ export async function createEps({ list, service }: any) {
|
|||||||
flags: "w"
|
flags: "w"
|
||||||
}).write(
|
}).write(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
list.map((e: any) => {
|
(options.list || []).map((e) => {
|
||||||
return [e.prefix, e.api.map((a: any) => [a.method || "", a.path, a.name || ""])];
|
const req = e.api.map((a) => {
|
||||||
|
const arr = [a.name ? `/${a.name}` : a.path];
|
||||||
|
|
||||||
|
if (a.method) {
|
||||||
|
arr.push(a.method);
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
});
|
||||||
|
|
||||||
|
return [e.prefix, e.name || "", req];
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1 +1 @@
|
|||||||
[["/admin/base/comm",[["post","/personUpdate",""],["get","/uploadMode",""],["get","/permmenu",""],["get","/person",""],["post","/upload",""],["post","/logout",""],["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/base/open",[["get","/refreshToken",""],["get","/captcha",""],["post","/login",""],["get","/html",""],["get","/eps",""],["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/base/sys/department",[["post","/delete",""],["post","/update",""],["post","/order",""],["post","/list",""],["post","/add",""],["","/page",""],["","/info",""]]],["/admin/base/sys/log",[["post","/setKeep",""],["get","/getKeep",""],["post","/clear",""],["post","/page",""],["","/list",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/admin/base/sys/menu",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/base/sys/param",[["post","/delete",""],["post","/update",""],["get","/html",""],["get","/info",""],["post","/page",""],["post","/add",""],["","/list",""]]],["/admin/base/sys/role",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/base/sys/user",[["post","/delete",""],["post","/update",""],["post","/move",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/demo/goods",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/page",""],["post","/list",""],["post","/add",""]]],["/admin/dict/info",[["post","/delete",""],["post","/update",""],["post","/data",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/dict/type",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/space/info",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/space/type",[["post","/delete",""],["post","/update",""],["get","/info",""],["post","/list",""],["post","/page",""],["post","/add",""]]],["/admin/task/info",[["post","/delete",""],["post","/update",""],["post","/start",""],["post","/once",""],["post","/stop",""],["get","/info",""],["post","/page",""],["get","/log",""],["post","/add",""],["","/list",""]]],["/chat/message",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/chat/session",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]],["/test",[["","/list",""],["","/page",""],["","/info",""],["","/update",""],["","/delete",""],["","/add",""]]]]
|
[["/admin/base/comm","",[["/personUpdate","post"],["/uploadMode","get"],["/permmenu","get"],["/person","get"],["/upload","post"],["/logout","post"],["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/admin/base/open","",[["/refreshToken","get"],["/captcha","get"],["/login","post"],["/html","get"],["/eps","get"],["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/admin/base/sys/department","BaseSysDepartmentEntity",[["/delete","post"],["/update","post"],["/order","post"],["/list","post"],["/add","post"],["/page"],["/info"]]],["/admin/base/sys/log","BaseSysLogEntity",[["/setKeep","post"],["/getKeep","get"],["/clear","post"],["/page","post"],["/list"],["/info"],["/update"],["/delete"],["/add"]]],["/admin/base/sys/menu","BaseSysMenuEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/base/sys/param","BaseSysParamEntity",[["/delete","post"],["/update","post"],["/html","get"],["/info","get"],["/page","post"],["/add","post"],["/list"]]],["/admin/base/sys/role","BaseSysRoleEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/base/sys/user","BaseSysUserEntity",[["/delete","post"],["/update","post"],["/move","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/demo/goods","DemoGoodsEntity",[["/delete","post"],["/update","post"],["/info","get"],["/page","post"],["/list","post"],["/add","post"]]],["/admin/dict/info","DictInfoEntity",[["/delete","post"],["/update","post"],["/data","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/dict/type","DictTypeEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/space/info","SpaceInfoEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/space/type","SpaceTypeEntity",[["/delete","post"],["/update","post"],["/info","get"],["/list","post"],["/page","post"],["/add","post"]]],["/admin/task/info","TaskInfoEntity",[["/delete","post"],["/update","post"],["/start","post"],["/once","post"],["/stop","post"],["/info","get"],["/page","post"],["/log","get"],["/add","post"],["/list"]]],["/chat/message","",[["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/chat/session","",[["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]],["/test","",[["/list"],["/page"],["/info"],["/update"],["/delete"],["/add"]]]]
|
@ -1,5 +1,5 @@
|
|||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
import { isAbsolute, join, relative, sep } from "path";
|
import { join, sep } from "path";
|
||||||
|
|
||||||
// 首字母大写
|
// 首字母大写
|
||||||
export function firstUpperCase(value: string): string {
|
export function firstUpperCase(value: string): string {
|
||||||
@ -30,7 +30,7 @@ export function readFile(name: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 解析body
|
// 解析body
|
||||||
export function parseJson(req: any) {
|
export function parseJson(req: any): Promise<any> {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
let d = "";
|
let d = "";
|
||||||
req.on("data", function (chunk: Buffer) {
|
req.on("data", function (chunk: Buffer) {
|
||||||
|
24
package.json
24
package.json
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "front-next",
|
"name": "front-next",
|
||||||
"version": "5.11.2",
|
"version": "5.12.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite --host",
|
"dev": "vite --host",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
@ -9,25 +9,31 @@
|
|||||||
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix"
|
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@cool-vue/admin": "^5.0.3",
|
"@codemirror/lang-javascript": "^6.1.2",
|
||||||
"@cool-vue/crud": "^5.7.1",
|
"@codemirror/theme-one-dark": "^6.1.0",
|
||||||
"@element-plus/icons-vue": "^2.0.6",
|
"@cool-vue/crud": "^5.9.4",
|
||||||
|
"@element-plus/icons-vue": "^2.0.10",
|
||||||
"@vueuse/core": "^9.1.0",
|
"@vueuse/core": "^9.1.0",
|
||||||
|
"@wangeditor/editor": "^5.1.23",
|
||||||
|
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||||
"axios": "^0.27.2",
|
"axios": "^0.27.2",
|
||||||
|
"codemirror": "^6.0.1",
|
||||||
"core-js": "^3.23.5",
|
"core-js": "^3.23.5",
|
||||||
"echarts": "^5.3.3",
|
"echarts": "^5.3.3",
|
||||||
"element-plus": "^2.2.17",
|
"element-plus": "^2.2.28",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"mitt": "^3.0.0",
|
"mitt": "^3.0.0",
|
||||||
"mockjs": "^1.1.0",
|
"mockjs": "^1.1.0",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
"pinia": "^2.0.16",
|
"pinia": "^2.0.28",
|
||||||
|
"quill": "^1.3.7",
|
||||||
"socket.io-client": "^4.5.1",
|
"socket.io-client": "^4.5.1",
|
||||||
"store": "^2.0.12",
|
"store": "^2.0.12",
|
||||||
"vue": "^3.2.40",
|
"vue": "^3.2.45",
|
||||||
|
"vue-codemirror": "^6.1.1",
|
||||||
"vue-echarts": "^6.2.3",
|
"vue-echarts": "^6.2.3",
|
||||||
"vue-router": "^4.1.2",
|
"vue-router": "^4.1.6",
|
||||||
"vuedraggable": "^4.1.0",
|
"vuedraggable": "^4.1.0",
|
||||||
"xlsx": "^0.18.5"
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
@ -57,7 +63,7 @@
|
|||||||
"sass": "^1.53.0",
|
"sass": "^1.53.0",
|
||||||
"sass-loader": "^13.0.2",
|
"sass-loader": "^13.0.2",
|
||||||
"typescript": "^4.7.4",
|
"typescript": "^4.7.4",
|
||||||
"vite": "^3.0.6",
|
"vite": "^4.0.3",
|
||||||
"vite-plugin-compression": "^0.5.1"
|
"vite-plugin-compression": "^0.5.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,27 +69,7 @@ export async function createEps() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取
|
// 设置
|
||||||
async function getEps() {
|
|
||||||
if (config.test.eps) {
|
|
||||||
await service
|
|
||||||
.request({
|
|
||||||
url: "/admin/base/open/eps"
|
|
||||||
})
|
|
||||||
.then((res) => {
|
|
||||||
if (!isEmpty(res)) {
|
|
||||||
set(res);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((err) => {
|
|
||||||
console.error("[Eps] 获取失败!", err.message);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
set();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 设置、创建
|
|
||||||
async function set(d?: any) {
|
async function set(d?: any) {
|
||||||
const list: any[] = [];
|
const list: any[] = [];
|
||||||
|
|
||||||
@ -131,12 +111,11 @@ export async function createEps() {
|
|||||||
// 创建方法
|
// 创建方法
|
||||||
e.api.forEach((a: any) => {
|
e.api.forEach((a: any) => {
|
||||||
// 方法名
|
// 方法名
|
||||||
const n = (a.name || a.path).replace("/", "");
|
const n = a.path.replace("/", "");
|
||||||
|
|
||||||
// 过滤
|
// 过滤
|
||||||
if (!names.includes(n)) {
|
if (!names.includes(n)) {
|
||||||
// 本地不存在则创建
|
// 本地不存在则创建
|
||||||
|
|
||||||
if (!d[k][n]) {
|
if (!d[k][n]) {
|
||||||
if (n && !/[-:]/g.test(n)) {
|
if (n && !/[-:]/g.test(n)) {
|
||||||
d[k][n] = function (data: any) {
|
d[k][n] = function (data: any) {
|
||||||
@ -183,28 +162,43 @@ export async function createEps() {
|
|||||||
createDts(list);
|
createDts(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用 eps.json
|
// 获取
|
||||||
|
async function getEps() {
|
||||||
try {
|
try {
|
||||||
const eps = JSON.parse(__EPS__ || "[]").map(([prefix, api]: any[]) => {
|
// 本地数据
|
||||||
|
let list = JSON.parse(__EPS__ || "[]").map(([prefix, name, api]: any[]) => {
|
||||||
return {
|
return {
|
||||||
prefix,
|
prefix,
|
||||||
api: api.map(([method, path, name]: string[]) => {
|
name,
|
||||||
|
api: api.map(([path, method]: string[]) => {
|
||||||
return {
|
return {
|
||||||
method,
|
method,
|
||||||
path,
|
path
|
||||||
name
|
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
set(eps);
|
// 接口数据
|
||||||
} catch (err) {
|
if (isDev && config.test.eps) {
|
||||||
console.error("[Eps] 解析失败!", err);
|
await service
|
||||||
|
.request({
|
||||||
|
url: "/admin/base/open/eps"
|
||||||
|
})
|
||||||
|
.then((res) => {
|
||||||
|
if (!isEmpty(res)) {
|
||||||
|
list = res;
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 开发环境下使用接口 /eps 刷新数据
|
if (list) {
|
||||||
if (isDev) {
|
set(list);
|
||||||
await getEps();
|
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[Eps] 获取失败!", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await getEps();
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { Emitter } from "mitt";
|
import { Emitter } from "mitt";
|
||||||
import { onBeforeUpdate, ref, inject } from "vue";
|
import { onBeforeUpdate, ref, inject } from "vue";
|
||||||
|
import { isNumber } from "lodash-es";
|
||||||
import { useRoute, useRouter } from "vue-router";
|
import { useRoute, useRouter } from "vue-router";
|
||||||
import { service } from "../service";
|
import { service } from "../service";
|
||||||
import { Data } from "../utils";
|
import { Data } from "../utils";
|
||||||
@ -22,6 +23,16 @@ export function useRefs() {
|
|||||||
return { refs, setRefs };
|
return { refs, setRefs };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function useComm() {
|
||||||
|
function px(val: string | number) {
|
||||||
|
return isNumber(val) ? `${val}px` : val;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
px
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function useCool() {
|
export function useCool() {
|
||||||
return {
|
return {
|
||||||
service: useService(),
|
service: useService(),
|
||||||
|
6
src/cool/types/index.d.ts
vendored
6
src/cool/types/index.d.ts
vendored
@ -1,6 +1,8 @@
|
|||||||
import { App, Component, Directive } from "vue";
|
import { Component, Directive } from "vue";
|
||||||
import { Router as VueRouter, RouteRecordRaw } from "vue-router";
|
import { Router as VueRouter, RouteRecordRaw } from "vue-router";
|
||||||
|
|
||||||
|
export declare type Merge<A, B> = Omit<A, keyof B> & B;
|
||||||
|
|
||||||
export declare interface ModuleConfig {
|
export declare interface ModuleConfig {
|
||||||
order?: number;
|
order?: number;
|
||||||
options?: {
|
options?: {
|
||||||
@ -9,7 +11,7 @@ export declare interface ModuleConfig {
|
|||||||
components?: Component[];
|
components?: Component[];
|
||||||
views?: RouteRecordRaw[];
|
views?: RouteRecordRaw[];
|
||||||
pages?: RouteRecordRaw[];
|
pages?: RouteRecordRaw[];
|
||||||
install?(app: App, options?: { [key: string]: any }): void;
|
install?(app: any, options?: any): any;
|
||||||
onLoad?(events: {
|
onLoad?(events: {
|
||||||
hasToken: (cb: () => Promise<any> | void) => Promise<any> | void;
|
hasToken: (cb: () => Promise<any> | void) => Promise<any> | void;
|
||||||
[key: string]: any;
|
[key: string]: any;
|
||||||
|
110
src/modules/base/components/avatar/index.vue
Normal file
110
src/modules/base/components/avatar/index.vue
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cl-avatar" :class="[size, shape]" :style="[style]">
|
||||||
|
<el-image :src="src || modelValue" alt="头像">
|
||||||
|
<template #error>
|
||||||
|
<div class="image-slot">
|
||||||
|
<el-icon :size="20" :color="color"><user /></el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, PropType } from "vue";
|
||||||
|
import { isNumber } from "lodash-es";
|
||||||
|
import { User } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-avatar",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
User
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: String,
|
||||||
|
src: String,
|
||||||
|
size: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 36
|
||||||
|
},
|
||||||
|
shape: {
|
||||||
|
type: String as PropType<"square" | "circle">,
|
||||||
|
default: "square"
|
||||||
|
},
|
||||||
|
backgroundColor: {
|
||||||
|
type: String,
|
||||||
|
default: "#f7f7f7"
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: "#ccc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const size = isNumber(props.size) ? props.size + "px" : props.size;
|
||||||
|
|
||||||
|
const style = computed(() => {
|
||||||
|
return {
|
||||||
|
height: size,
|
||||||
|
width: size,
|
||||||
|
backgroundColor: props.backgroundColor
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
style
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-avatar {
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0 auto;
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
height: 50px;
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.medium {
|
||||||
|
height: 40px;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
height: 30px;
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.circle {
|
||||||
|
border-radius: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.square {
|
||||||
|
border-radius: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.image-slot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
127
src/modules/base/components/codemirror/index.vue
Normal file
127
src/modules/base/components/codemirror/index.vue
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cl-codemirror" :class="{ disabled }">
|
||||||
|
<codemirror
|
||||||
|
ref="Editor"
|
||||||
|
v-model="content"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:style="{ height, fontSize }"
|
||||||
|
autofocus
|
||||||
|
:disabled="disabled"
|
||||||
|
indent-with-tab
|
||||||
|
:tab-size="4"
|
||||||
|
:extensions="extensions"
|
||||||
|
@change="onChange"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { Codemirror } from "vue-codemirror";
|
||||||
|
import { javascript } from "@codemirror/lang-javascript";
|
||||||
|
import { oneDark } from "@codemirror/theme-one-dark";
|
||||||
|
import { ref, watch, computed, defineComponent } from "vue";
|
||||||
|
import { useDark } from "@vueuse/core";
|
||||||
|
import { isNumber } from "lodash-es";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-codemirror",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: "请输入"
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 400
|
||||||
|
},
|
||||||
|
fontSize: {
|
||||||
|
type: String,
|
||||||
|
default: "14px"
|
||||||
|
},
|
||||||
|
disabled: Boolean
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ["update:modelValue", "change"],
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Codemirror
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const Editor = ref();
|
||||||
|
|
||||||
|
// 是否暗黑模式
|
||||||
|
const isDark = ref(useDark());
|
||||||
|
|
||||||
|
// 高度
|
||||||
|
const height = computed(() =>
|
||||||
|
isNumber(props.height) ? `${props.height}px` : props.height
|
||||||
|
);
|
||||||
|
|
||||||
|
// 插件
|
||||||
|
const extensions: any[] = [javascript()];
|
||||||
|
|
||||||
|
if (isDark.value) {
|
||||||
|
extensions.push(oneDark);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 内容
|
||||||
|
const content = ref("");
|
||||||
|
|
||||||
|
// 值改变
|
||||||
|
function onChange(value: string) {
|
||||||
|
emit("update:modelValue", value);
|
||||||
|
emit("change", value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听值
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
content.value = val;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
Editor,
|
||||||
|
isDark,
|
||||||
|
height,
|
||||||
|
content,
|
||||||
|
extensions,
|
||||||
|
onChange
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-codemirror {
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
:deep(.cm-editor) {
|
||||||
|
.cm-foldGutter {
|
||||||
|
width: 12px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.cm-focused {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
:deep(.cm-content) {
|
||||||
|
background-color: var(--el-disabled-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
141
src/modules/base/components/column-custom/index.vue
Normal file
141
src/modules/base/components/column-custom/index.vue
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cl-column-custom__wrap">
|
||||||
|
<el-button @click="open">自定义列</el-button>
|
||||||
|
|
||||||
|
<cl-dialog v-model="visible" title="自定义列">
|
||||||
|
<div class="cl-column-custom__dialog">
|
||||||
|
<div class="left">
|
||||||
|
<el-checkbox-group v-model="selection">
|
||||||
|
<el-checkbox
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="index"
|
||||||
|
:label="item.value"
|
||||||
|
>{{ item.label }}</el-checkbox
|
||||||
|
>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="right"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="close">取消</el-button>
|
||||||
|
<el-button type="success" @click="save">保存</el-button>
|
||||||
|
</template>
|
||||||
|
</cl-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, onMounted, ref } from "vue";
|
||||||
|
import { useCrud } from "@cool-vue/crud";
|
||||||
|
import store from "store";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-column-custom",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
name: String,
|
||||||
|
columns: {
|
||||||
|
type: Array,
|
||||||
|
required: true,
|
||||||
|
default: () => []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
// cl-crud
|
||||||
|
const Crud = useCrud();
|
||||||
|
|
||||||
|
// 是否可见
|
||||||
|
const visible = ref(false);
|
||||||
|
|
||||||
|
// 选中
|
||||||
|
const selection = ref([]);
|
||||||
|
|
||||||
|
// 名称
|
||||||
|
const name = `column-custom__${props.name || location.pathname}`;
|
||||||
|
|
||||||
|
// 列数据
|
||||||
|
const list = computed(() => {
|
||||||
|
return props.columns
|
||||||
|
.filter((e: any) => !e.type)
|
||||||
|
.map((e: any) => {
|
||||||
|
return {
|
||||||
|
label: e.label,
|
||||||
|
value: e.prop
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// 获取 prop
|
||||||
|
function getNames() {
|
||||||
|
const arr = store.get(name);
|
||||||
|
return arr ? arr : list.value.map((e) => e.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 改变列
|
||||||
|
function change() {
|
||||||
|
const names = getNames();
|
||||||
|
|
||||||
|
if (store.get(name)) {
|
||||||
|
Crud.value!["cl-table"].reBuild(() => {
|
||||||
|
props.columns.map((e: any) => {
|
||||||
|
if (!e.type) {
|
||||||
|
e.hidden = !names.includes(e.prop);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存
|
||||||
|
function save() {
|
||||||
|
store.set(name, selection.value);
|
||||||
|
change();
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 打开
|
||||||
|
function open() {
|
||||||
|
selection.value = getNames();
|
||||||
|
visible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关闭
|
||||||
|
function close() {
|
||||||
|
visible.value = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
change();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
visible,
|
||||||
|
list,
|
||||||
|
selection,
|
||||||
|
open,
|
||||||
|
close,
|
||||||
|
save
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-column-custom {
|
||||||
|
&__wrap {
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__dialog {
|
||||||
|
.left {
|
||||||
|
}
|
||||||
|
|
||||||
|
.right {
|
||||||
|
border-left: 1px solid #eee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
30
src/modules/base/components/date/text.vue
Normal file
30
src/modules/base/components/date/text.vue
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
<template>
|
||||||
|
<span class="cl-date-text">{{ value }}</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, computed } from "vue";
|
||||||
|
import dayjs from "dayjs";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-date-text",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: [String, Number],
|
||||||
|
format: {
|
||||||
|
type: String,
|
||||||
|
default: "YYYY-MM-DD HH:mm:ss"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const value = computed(() => {
|
||||||
|
return props.modelValue ? dayjs(props.modelValue).format(props.format) : "";
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
value
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
320
src/modules/base/components/editor/quill.vue
Normal file
320
src/modules/base/components/editor/quill.vue
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cl-editor-quill" :class="{ disabled }">
|
||||||
|
<div ref="Editor" class="editor"></div>
|
||||||
|
|
||||||
|
<!-- 图片 -->
|
||||||
|
<cl-upload-space
|
||||||
|
ref="ImageSpace"
|
||||||
|
accept="image/*"
|
||||||
|
:show-btn="false"
|
||||||
|
@confirm="onSpaceConfirm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 视频 -->
|
||||||
|
<cl-upload-space
|
||||||
|
ref="VideoSpace"
|
||||||
|
accept="video/*"
|
||||||
|
:show-btn="false"
|
||||||
|
@confirm="onSpaceConfirm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, onMounted, ref, watch } from "vue";
|
||||||
|
import Quill from "quill";
|
||||||
|
import "quill/dist/quill.snow.css";
|
||||||
|
import { useComm } from "/@/cool";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-editor-quill",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: null,
|
||||||
|
options: Object,
|
||||||
|
height: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 400
|
||||||
|
},
|
||||||
|
disabled: Boolean
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ["update:modelValue", "load"],
|
||||||
|
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { px } = useComm();
|
||||||
|
|
||||||
|
let quill: any = null;
|
||||||
|
|
||||||
|
// 编辑器
|
||||||
|
const Editor = ref();
|
||||||
|
|
||||||
|
// 图片上传
|
||||||
|
const ImageSpace = ref();
|
||||||
|
|
||||||
|
// 视频上传
|
||||||
|
const VideoSpace = ref();
|
||||||
|
|
||||||
|
// 文本内容
|
||||||
|
const content = ref("");
|
||||||
|
|
||||||
|
// 光标位置
|
||||||
|
const cursorIndex = ref(0);
|
||||||
|
|
||||||
|
// 上传处理
|
||||||
|
function uploadFileHandler(type: "image" | "video") {
|
||||||
|
if (props.disabled) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const selection = quill.getSelection();
|
||||||
|
|
||||||
|
if (selection) {
|
||||||
|
cursorIndex.value = selection.index;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == "image") {
|
||||||
|
ImageSpace.value.open();
|
||||||
|
} else if (type == "video") {
|
||||||
|
VideoSpace.value.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 文件确认
|
||||||
|
function onSpaceConfirm(files: any[]) {
|
||||||
|
if (files.length > 0) {
|
||||||
|
// 批量插入图片
|
||||||
|
files.forEach((file, i) => {
|
||||||
|
if (file.type == "image") {
|
||||||
|
quill.insertEmbed(
|
||||||
|
cursorIndex.value + i,
|
||||||
|
file.type,
|
||||||
|
file.url,
|
||||||
|
Quill.sources.USER
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 移动光标到图片后一位
|
||||||
|
quill.setSelection(cursorIndex.value + files.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置内容
|
||||||
|
function setContent(val: string) {
|
||||||
|
quill.root.innerHTML = val || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置高度
|
||||||
|
function setHeight() {
|
||||||
|
quill.container.style.height = px(props.height);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听绑定值
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val: string) => {
|
||||||
|
if (val) {
|
||||||
|
if (val !== content.value) {
|
||||||
|
setContent(val);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
setContent("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(function () {
|
||||||
|
// 实例化
|
||||||
|
quill = new Quill(Editor.value, {
|
||||||
|
theme: "snow",
|
||||||
|
placeholder: "输入内容",
|
||||||
|
modules: {
|
||||||
|
toolbar: [
|
||||||
|
["bold", "italic", "underline", "strike"],
|
||||||
|
["blockquote", "code-block"],
|
||||||
|
[{ header: 1 }, { header: 2 }],
|
||||||
|
[{ list: "ordered" }, { list: "bullet" }],
|
||||||
|
[{ script: "sub" }, { script: "super" }],
|
||||||
|
[{ indent: "-1" }, { indent: "+1" }],
|
||||||
|
[{ direction: "rtl" }],
|
||||||
|
[{ size: ["small", false, "large", "huge"] }],
|
||||||
|
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
||||||
|
[{ color: [] }, { background: [] }],
|
||||||
|
[{ font: [] }],
|
||||||
|
[{ align: [] }],
|
||||||
|
["clean"],
|
||||||
|
["link", "video", "image"]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
...props.options
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加图片工具
|
||||||
|
quill.getModule("toolbar").addHandler("image", () => {
|
||||||
|
uploadFileHandler("image");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加视频工具
|
||||||
|
quill.getModule("toolbar").addHandler("video", () => {
|
||||||
|
uploadFileHandler("video");
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听输入
|
||||||
|
quill.on("text-change", () => {
|
||||||
|
content.value = quill.root.innerHTML;
|
||||||
|
emit("update:modelValue", content.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 设置内容
|
||||||
|
setContent(props.modelValue);
|
||||||
|
|
||||||
|
// 设置高度
|
||||||
|
setHeight();
|
||||||
|
|
||||||
|
// 是否禁用
|
||||||
|
quill.enable(!props.disabled);
|
||||||
|
|
||||||
|
// 加载回调
|
||||||
|
emit("load", quill);
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
Editor,
|
||||||
|
ImageSpace,
|
||||||
|
VideoSpace,
|
||||||
|
content,
|
||||||
|
quill,
|
||||||
|
cursorIndex,
|
||||||
|
setContent,
|
||||||
|
onSpaceConfirm
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-editor-quill {
|
||||||
|
background-color: #fff;
|
||||||
|
line-height: normal;
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
:deep(.ql-editor) {
|
||||||
|
background-color: var(--el-disabled-bg-color);
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ql-stroke) {
|
||||||
|
stroke: var(--el-disabled-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ql-fill) {
|
||||||
|
fill: var(--el-disabled-text-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ql-picker) {
|
||||||
|
color: var(--el-disabled-text-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload,
|
||||||
|
#quill-upload-btn {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ql-toolbar) {
|
||||||
|
border-color: var(--el-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow {
|
||||||
|
border-color: var(--el-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-tooltip[data-mode="link"]::before {
|
||||||
|
content: "请输入链接地址:";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-tooltip.ql-editing a.ql-action::after {
|
||||||
|
border-right: 0px;
|
||||||
|
content: "保存";
|
||||||
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-tooltip[data-mode="video"]::before {
|
||||||
|
content: "请输入视频地址:";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-label::before,
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-item::before {
|
||||||
|
content: "14px";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
|
||||||
|
content: "10px";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
|
||||||
|
content: "18px";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
|
||||||
|
content: "32px";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item::before {
|
||||||
|
content: "文本";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
|
||||||
|
content: "标题1";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
|
||||||
|
content: "标题2";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
|
||||||
|
content: "标题3";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
|
||||||
|
content: "标题4";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
|
||||||
|
content: "标题5";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
|
||||||
|
content: "标题6";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-label::before,
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-item::before {
|
||||||
|
content: "标准字体";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
|
||||||
|
content: "衬线字体";
|
||||||
|
}
|
||||||
|
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
|
||||||
|
.ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
|
||||||
|
content: "等宽字体";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
202
src/modules/base/components/editor/wang.vue
Normal file
202
src/modules/base/components/editor/wang.vue
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cl-editor-wang" :class="{ disabled }">
|
||||||
|
<!-- 工具栏 -->
|
||||||
|
<toolbar :editor="editorRef" :mode="mode" />
|
||||||
|
|
||||||
|
<!-- 编辑框 -->
|
||||||
|
<editor
|
||||||
|
v-model="value"
|
||||||
|
:defaultConfig="editorConfig"
|
||||||
|
:mode="mode"
|
||||||
|
:style="style"
|
||||||
|
@onCreated="onCreated"
|
||||||
|
@onFocus="onFocus"
|
||||||
|
@onBlur="onBlur"
|
||||||
|
@onChange="onChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 图片 -->
|
||||||
|
<cl-upload-space
|
||||||
|
ref="ImageSpace"
|
||||||
|
accept="image/*"
|
||||||
|
:show-btn="false"
|
||||||
|
@confirm="onSpaceConfirm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 视频 -->
|
||||||
|
<cl-upload-space
|
||||||
|
ref="VideoSpace"
|
||||||
|
accept="video/*"
|
||||||
|
:show-btn="false"
|
||||||
|
@confirm="onSpaceConfirm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import "@wangeditor/editor/dist/css/style.css";
|
||||||
|
import {
|
||||||
|
onBeforeUnmount,
|
||||||
|
ref,
|
||||||
|
shallowRef,
|
||||||
|
watch,
|
||||||
|
PropType,
|
||||||
|
reactive,
|
||||||
|
computed,
|
||||||
|
defineComponent
|
||||||
|
} from "vue";
|
||||||
|
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
|
||||||
|
import { IEditorConfig } from "@wangeditor/editor";
|
||||||
|
import { useComm } from "/@/cool";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-editor-wang",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
Editor,
|
||||||
|
Toolbar
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: String,
|
||||||
|
mode: {
|
||||||
|
type: String as PropType<"default" | "simple">,
|
||||||
|
default: "default"
|
||||||
|
},
|
||||||
|
height: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 400
|
||||||
|
},
|
||||||
|
disabled: Boolean
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ["update:modelValue", "change", "focus", "blur"],
|
||||||
|
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const { px } = useComm();
|
||||||
|
|
||||||
|
// 图片上传
|
||||||
|
const ImageSpace = ref();
|
||||||
|
|
||||||
|
// 视频上传
|
||||||
|
const VideoSpace = ref();
|
||||||
|
|
||||||
|
// 编辑器
|
||||||
|
const editorRef = shallowRef();
|
||||||
|
|
||||||
|
// 内容
|
||||||
|
const value = ref();
|
||||||
|
|
||||||
|
// 编辑器样式
|
||||||
|
const style = computed(() => {
|
||||||
|
return {
|
||||||
|
height: px(props.height)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
value.value = val;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
function onCreated(editor: any) {
|
||||||
|
editorRef.value = editor;
|
||||||
|
|
||||||
|
if (props.disabled) {
|
||||||
|
editor.disable();
|
||||||
|
} else {
|
||||||
|
editor.enable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function onFocus(editor: any) {
|
||||||
|
emit("focus", editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBlur(editor: any) {
|
||||||
|
emit("blur", editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange() {
|
||||||
|
emit("update:modelValue", value.value);
|
||||||
|
emit("change", value.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
const temp = reactive<any>({
|
||||||
|
insertFn: null
|
||||||
|
});
|
||||||
|
|
||||||
|
// 配置
|
||||||
|
const editorConfig: Partial<IEditorConfig> = {
|
||||||
|
placeholder: "请输入",
|
||||||
|
MENU_CONF: {
|
||||||
|
uploadImage: {
|
||||||
|
customBrowseAndUpload(insertFn: any) {
|
||||||
|
temp.insertFn = insertFn;
|
||||||
|
ImageSpace.value.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
uploadVideo: {
|
||||||
|
customBrowseAndUpload(insertFn: any) {
|
||||||
|
temp.insertFn = insertFn;
|
||||||
|
VideoSpace.value.open();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 文件确认
|
||||||
|
function onSpaceConfirm(files: any[]) {
|
||||||
|
if (files.length > 0) {
|
||||||
|
files.forEach((file) => {
|
||||||
|
temp.insertFn(file.url);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
const editor = editorRef.value;
|
||||||
|
if (editor == null) return;
|
||||||
|
editor.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
ImageSpace,
|
||||||
|
VideoSpace,
|
||||||
|
editorRef,
|
||||||
|
value,
|
||||||
|
style,
|
||||||
|
onCreated,
|
||||||
|
onFocus,
|
||||||
|
onBlur,
|
||||||
|
onChange,
|
||||||
|
editorConfig,
|
||||||
|
onSpaceConfirm
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-editor-wang {
|
||||||
|
border: 1px solid var(--el-border-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
overflow: hidden;
|
||||||
|
line-height: normal;
|
||||||
|
|
||||||
|
:deep(.w-e-toolbar) {
|
||||||
|
border-bottom: 1px solid var(--el-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.disabled {
|
||||||
|
:deep(.w-e-text-container) {
|
||||||
|
background-color: var(--el-disabled-bg-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
53
src/modules/base/components/icon/svg.vue
Normal file
53
src/modules/base/components/icon/svg.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<svg :class="svgClass" :style="style" aria-hidden="true">
|
||||||
|
<use :xlink:href="iconName" />
|
||||||
|
</svg>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent, ref } from "vue";
|
||||||
|
import { isNumber } from "lodash-es";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-svg",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
name: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
className: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
type: [String, Number]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const style = ref({
|
||||||
|
fontSize: isNumber(props.size) ? props.size + "px" : props.size
|
||||||
|
});
|
||||||
|
|
||||||
|
const iconName = computed(() => `#icon-${props.name}`);
|
||||||
|
const svgClass = computed(() => {
|
||||||
|
return ["cl-svg", `cl-svg__${props.name}`, String(props.className || "")];
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
style,
|
||||||
|
iconName,
|
||||||
|
svgClass
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-svg {
|
||||||
|
width: 1em;
|
||||||
|
height: 1em;
|
||||||
|
vertical-align: -0.15em;
|
||||||
|
fill: currentColor;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
</style>
|
112
src/modules/base/components/image/index.vue
Normal file
112
src/modules/base/components/image/index.vue
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
class="cl-image"
|
||||||
|
:style="{
|
||||||
|
justifyContent: justify,
|
||||||
|
height: style.h
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<el-image
|
||||||
|
:src="urls[0]"
|
||||||
|
:fit="fit"
|
||||||
|
:lazy="lazy"
|
||||||
|
:preview-src-list="urls"
|
||||||
|
:style="{
|
||||||
|
height: style.h,
|
||||||
|
width: style.w
|
||||||
|
}"
|
||||||
|
preview-teleported
|
||||||
|
>
|
||||||
|
<template #error>
|
||||||
|
<div class="image-slot">
|
||||||
|
<el-icon :size="20"><picture-filled /></el-icon>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { computed, defineComponent } from "vue";
|
||||||
|
import { isArray, isNumber, isString } from "lodash-es";
|
||||||
|
import { PictureFilled } from "@element-plus/icons-vue";
|
||||||
|
import { useComm } from "/@/cool";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-image",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
PictureFilled
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: [String, Array],
|
||||||
|
src: [String, Array],
|
||||||
|
size: {
|
||||||
|
type: [Number, Array],
|
||||||
|
default: 100
|
||||||
|
},
|
||||||
|
lazy: Boolean,
|
||||||
|
fit: {
|
||||||
|
type: String,
|
||||||
|
default: "cover"
|
||||||
|
},
|
||||||
|
justify: {
|
||||||
|
type: String,
|
||||||
|
default: "center"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const { px } = useComm();
|
||||||
|
|
||||||
|
const urls = computed(() => {
|
||||||
|
const urls: any = props.modelValue || props.src;
|
||||||
|
|
||||||
|
if (isArray(urls)) {
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isString(urls)) {
|
||||||
|
return (urls || "").split(",").filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
const style = computed(() => {
|
||||||
|
const [h, w]: any = isNumber(props.size) ? [props.size, props.size] : props.size;
|
||||||
|
|
||||||
|
return {
|
||||||
|
h: px(h),
|
||||||
|
w: px(w)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
urls,
|
||||||
|
style
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-image {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.el-image {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
.image-slot {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
78
src/modules/base/components/link/index.vue
Normal file
78
src/modules/base/components/link/index.vue
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<template>
|
||||||
|
<a v-for="item in urls" :key="item" class="cl-link" :href="item" :target="target">
|
||||||
|
<el-icon><icon-link /></el-icon>{{ filename(item) }}
|
||||||
|
</a>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, computed } from "vue";
|
||||||
|
import { isArray, isString, last } from "lodash-es";
|
||||||
|
import { Link } from "@element-plus/icons-vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-link",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
"icon-link": Link
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: [String, Array],
|
||||||
|
href: [String, Array],
|
||||||
|
text: {
|
||||||
|
type: String,
|
||||||
|
default: "查看"
|
||||||
|
},
|
||||||
|
target: {
|
||||||
|
type: String,
|
||||||
|
default: "_blank"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
setup(props) {
|
||||||
|
const urls = computed(() => {
|
||||||
|
const urls: any = props.modelValue || props.href;
|
||||||
|
|
||||||
|
if (isArray(urls)) {
|
||||||
|
return urls;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isString(urls)) {
|
||||||
|
return (urls || "").split(",").filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
});
|
||||||
|
|
||||||
|
function filename(url: string) {
|
||||||
|
return last(url.split("/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
urls,
|
||||||
|
filename
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-link {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
text-align: left;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
padding: 0 5px;
|
||||||
|
border-radius: 5px;
|
||||||
|
margin: 2px;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
margin-right: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
65
src/modules/base/components/select/index.vue
Normal file
65
src/modules/base/components/select/index.vue
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
<template>
|
||||||
|
<el-select v-model="value" @change="onChange" clearable>
|
||||||
|
<el-option
|
||||||
|
v-for="(item, index) in list"
|
||||||
|
:key="index"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { useCrud } from "@cool-vue/crud";
|
||||||
|
import { computed, defineComponent, isRef, ref, watch } from "vue";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-select",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
modelValue: [String, Number],
|
||||||
|
options: {
|
||||||
|
type: [Array, Object],
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
prop: String
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ["update:modelValue", "change"],
|
||||||
|
|
||||||
|
setup(props, { emit }) {
|
||||||
|
// cl-crud
|
||||||
|
const Crud = useCrud();
|
||||||
|
const value = ref();
|
||||||
|
const list = computed<any>(() =>
|
||||||
|
isRef(props.options) ? props.options.value : props.options
|
||||||
|
);
|
||||||
|
|
||||||
|
// 值改变
|
||||||
|
function onChange(val: string) {
|
||||||
|
emit("update:modelValue", val);
|
||||||
|
emit("change", val);
|
||||||
|
|
||||||
|
if (props.prop) {
|
||||||
|
Crud.value?.refresh({ page: 1, [props.prop]: val === "" ? undefined : val });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val) => {
|
||||||
|
value.value = val;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
list,
|
||||||
|
value,
|
||||||
|
onChange
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
90
src/modules/base/components/switch/index.tsx
Normal file
90
src/modules/base/components/switch/index.tsx
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { useCrud } from "@cool-vue/crud";
|
||||||
|
import { ElMessage } from "element-plus";
|
||||||
|
import { defineComponent, ref, watch } from "vue";
|
||||||
|
import { isBoolean, isFunction } from "lodash-es";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-switch",
|
||||||
|
|
||||||
|
props: {
|
||||||
|
scope: null,
|
||||||
|
column: null,
|
||||||
|
modelValue: [Number, String, Boolean],
|
||||||
|
api: Function,
|
||||||
|
activeValue: {
|
||||||
|
type: [Number, String, Boolean],
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
inactiveValue: {
|
||||||
|
type: [Number, String, Boolean],
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: ["update:modelValue", "change"],
|
||||||
|
|
||||||
|
setup(props, { emit }) {
|
||||||
|
// cl-crud
|
||||||
|
const Crud = useCrud();
|
||||||
|
|
||||||
|
// 状态
|
||||||
|
const status = ref<boolean | number | string>();
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val: any) => {
|
||||||
|
if (isBoolean(props.activeValue)) {
|
||||||
|
status.value = Boolean(val);
|
||||||
|
} else {
|
||||||
|
status.value = val;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// 监听改变
|
||||||
|
function onChange(val: boolean | string | number) {
|
||||||
|
if (props.column && props.scope) {
|
||||||
|
const params = {
|
||||||
|
...props.scope,
|
||||||
|
[props.column.property]: val
|
||||||
|
};
|
||||||
|
|
||||||
|
const req = isFunction(props.api)
|
||||||
|
? props.api(params)
|
||||||
|
: Crud.value?.service.update(params);
|
||||||
|
|
||||||
|
if (req) {
|
||||||
|
req.then(() => {
|
||||||
|
ElMessage.success("更新成功");
|
||||||
|
emit("update:modelValue", val);
|
||||||
|
emit("change", val);
|
||||||
|
}).catch((err: any) => {
|
||||||
|
ElMessage.error(err.message);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
emit("update:modelValue", val);
|
||||||
|
emit("change", val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
status,
|
||||||
|
onChange
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
render(ctx: any) {
|
||||||
|
return (
|
||||||
|
<el-switch
|
||||||
|
v-model={ctx.status}
|
||||||
|
active-value={ctx.activeValue}
|
||||||
|
inactive-value={ctx.inactiveValue}
|
||||||
|
onChange={ctx.onChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
163
src/modules/base/components/view/group.vue
Normal file
163
src/modules/base/components/view/group.vue
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<template>
|
||||||
|
<div class="cl-view-group">
|
||||||
|
<div class="cl-view-group__wrap">
|
||||||
|
<!-- 组 -->
|
||||||
|
<div class="cl-view-group__left" :class="[isExpand ? '_expand' : '_collapse']">
|
||||||
|
<slot name="left"></slot>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="cl-view-group__right">
|
||||||
|
<div class="cl-view-group__right-head">
|
||||||
|
<div class="icon" @click="toExpand()">
|
||||||
|
<el-icon v-if="isExpand"><arrow-left /></el-icon>
|
||||||
|
<el-icon v-else><arrow-right /></el-icon>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span>{{ title }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="cl-view-group__right-content">
|
||||||
|
<slot name="right"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts">
|
||||||
|
import { defineComponent, provide, ref, watch } from "vue";
|
||||||
|
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
|
||||||
|
import { useStore } from "../../store";
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "cl-view-group",
|
||||||
|
|
||||||
|
components: {
|
||||||
|
ArrowLeft,
|
||||||
|
ArrowRight
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
title: String
|
||||||
|
},
|
||||||
|
|
||||||
|
setup() {
|
||||||
|
const { app } = useStore();
|
||||||
|
|
||||||
|
// 是否展开
|
||||||
|
const isExpand = ref(true);
|
||||||
|
|
||||||
|
// 收起、展开
|
||||||
|
function toExpand(value?: boolean) {
|
||||||
|
isExpand.value = value === undefined ? !isExpand.value : value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小屏幕下
|
||||||
|
function checkExpand(value?: boolean) {
|
||||||
|
if (app.browser.isMini) {
|
||||||
|
toExpand(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听屏幕大小变化
|
||||||
|
watch(
|
||||||
|
() => app.browser.isMini,
|
||||||
|
(val: boolean) => {
|
||||||
|
isExpand.value = !val;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
provide("viewGroup", {
|
||||||
|
checkExpand
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
isExpand,
|
||||||
|
toExpand,
|
||||||
|
checkExpand
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.cl-view-group {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
&__wrap {
|
||||||
|
display: flex;
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
background-color: var(--el-bg-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
&__left {
|
||||||
|
height: 100%;
|
||||||
|
width: 300px;
|
||||||
|
transition: width 0.3s;
|
||||||
|
flex-shrink: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-right: 1px solid var(--el-border-color);
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
&._collapse {
|
||||||
|
margin-right: 0;
|
||||||
|
width: 0 !important;
|
||||||
|
border-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__right {
|
||||||
|
width: calc(100% - 310px);
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
height: 40px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
cursor: pointer;
|
||||||
|
height: 40px;
|
||||||
|
width: 80px;
|
||||||
|
padding-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&-content {
|
||||||
|
height: calc(100% - 40px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media only screen and (max-width: 768px) {
|
||||||
|
.cl-view-group__left {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl-view-group__right {
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,13 +1,11 @@
|
|||||||
import { ModuleConfig, config } from "/@/cool";
|
import { ModuleConfig, config } from "/@/cool";
|
||||||
import { useStore } from "./store";
|
import { useStore } from "./store";
|
||||||
import { App } from "vue";
|
|
||||||
import Admin from "@cool-vue/admin";
|
|
||||||
import "@cool-vue/admin/dist/index.css";
|
|
||||||
import "./static/css/index.scss";
|
import "./static/css/index.scss";
|
||||||
|
|
||||||
export default (): ModuleConfig => {
|
export default (): ModuleConfig => {
|
||||||
return {
|
return {
|
||||||
order: 99,
|
order: 99,
|
||||||
|
components: Object.values(import.meta.glob("./components/**/*")),
|
||||||
views: [
|
views: [
|
||||||
{
|
{
|
||||||
path: "/my/info",
|
path: "/my/info",
|
||||||
@ -27,41 +25,38 @@ export default (): ModuleConfig => {
|
|||||||
meta: {
|
meta: {
|
||||||
process: false
|
process: false
|
||||||
},
|
},
|
||||||
component: () => import("./pages/error-page/401.vue")
|
component: () => import("./pages/error/401.vue")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/403",
|
path: "/403",
|
||||||
meta: {
|
meta: {
|
||||||
process: false
|
process: false
|
||||||
},
|
},
|
||||||
component: () => import("./pages/error-page/403.vue")
|
component: () => import("./pages/error/403.vue")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/404",
|
path: "/404",
|
||||||
meta: {
|
meta: {
|
||||||
process: false
|
process: false
|
||||||
},
|
},
|
||||||
component: () => import("./pages/error-page/404.vue")
|
component: () => import("./pages/error/404.vue")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/500",
|
path: "/500",
|
||||||
meta: {
|
meta: {
|
||||||
process: false
|
process: false
|
||||||
},
|
},
|
||||||
component: () => import("./pages/error-page/500.vue")
|
component: () => import("./pages/error/500.vue")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/502",
|
path: "/502",
|
||||||
meta: {
|
meta: {
|
||||||
process: false
|
process: false
|
||||||
},
|
},
|
||||||
component: () => import("./pages/error-page/502.vue")
|
component: () => import("./pages/error/502.vue")
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
install(app: App) {
|
install() {
|
||||||
// 基础库
|
|
||||||
app.use(Admin);
|
|
||||||
|
|
||||||
// 设置标题
|
// 设置标题
|
||||||
document.title = config.app.name;
|
document.title = config.app.name;
|
||||||
},
|
},
|
||||||
|
@ -10,7 +10,7 @@ export const useAppStore = defineStore("app", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// 浏览器信息
|
// 浏览器信息
|
||||||
const browser = ref<any>(getBrowser());
|
const browser = ref(getBrowser());
|
||||||
|
|
||||||
// 是否折叠
|
// 是否折叠
|
||||||
const isFold = ref(browser.value.isMini || false);
|
const isFold = ref(browser.value.isMini || false);
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
import { ModuleConfig } from "/@/cool";
|
import { Merge, ModuleConfig } from "/@/cool";
|
||||||
import Crud from "@cool-vue/crud";
|
import Crud from "@cool-vue/crud";
|
||||||
import "@cool-vue/crud/dist/index.css";
|
import "@cool-vue/crud/dist/index.css";
|
||||||
|
|
||||||
export default (): ModuleConfig => {
|
export default (): Merge<ModuleConfig, CrudOptions> => {
|
||||||
return {
|
return {
|
||||||
options: {
|
options: {
|
||||||
crud: {
|
|
||||||
dict: {
|
dict: {
|
||||||
sort: {
|
sort: {
|
||||||
prop: "order",
|
prop: "order",
|
||||||
order: "sort"
|
order: "sort"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
},
|
||||||
install: Crud.install
|
install: Crud.install
|
||||||
};
|
};
|
||||||
|
@ -36,10 +36,7 @@ const { dict } = useDict();
|
|||||||
|
|
||||||
const Crud = useCrud(
|
const Crud = useCrud(
|
||||||
{
|
{
|
||||||
service: "test",
|
service: "test"
|
||||||
async onRefresh(params, { next }) {
|
|
||||||
console.log(await next(params));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
(app) => {
|
(app) => {
|
||||||
app.refresh();
|
app.refresh();
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
<cl-editor-wang v-model="w" :height="400" />
|
<cl-editor-wang v-model="w" :height="400" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
<el-tab-pane label="Quill">
|
<el-tab-pane label="Quill" lazy>
|
||||||
<cl-editor-quill v-model="q" :height="400" />
|
<cl-editor-quill v-model="q" :height="400" />
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
</el-tabs>
|
</el-tabs>
|
||||||
|
@ -102,7 +102,7 @@ function clearDark() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 设置颜色
|
// 设置颜色
|
||||||
function setColor(color: string) {
|
function setColor(color: any) {
|
||||||
setTheme({ color });
|
setTheme({ color });
|
||||||
clearDark();
|
clearDark();
|
||||||
}
|
}
|
||||||
|
@ -127,9 +127,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<cl-dialog v-model="pv.visible" title="图片预览" width="500px">
|
<el-image-viewer
|
||||||
<img style="width: 100%" :src="pv.url" />
|
v-if="pv.visible"
|
||||||
</cl-dialog>
|
:url-list="pv.urls"
|
||||||
|
:initial-index="pv.index"
|
||||||
|
infinite
|
||||||
|
teleported
|
||||||
|
@close="
|
||||||
|
() => {
|
||||||
|
pv.visible = false;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
></el-image-viewer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="cl-upload">
|
<script lang="ts" setup name="cl-upload">
|
||||||
@ -230,7 +239,8 @@ const headers = computed(() => {
|
|||||||
// 预览
|
// 预览
|
||||||
const pv = reactive<any>({
|
const pv = reactive<any>({
|
||||||
visible: false,
|
visible: false,
|
||||||
url: ""
|
urls: [],
|
||||||
|
index: 0
|
||||||
});
|
});
|
||||||
|
|
||||||
// 列表
|
// 列表
|
||||||
@ -314,7 +324,8 @@ function clear() {
|
|||||||
function preview(item: Item) {
|
function preview(item: Item) {
|
||||||
if (item.type == "image") {
|
if (item.type == "image") {
|
||||||
pv.visible = true;
|
pv.visible = true;
|
||||||
pv.url = item.url;
|
pv.urls = list.value.map((e) => e.preload);
|
||||||
|
pv.index = pv.urls.indexOf(item.preload);
|
||||||
} else {
|
} else {
|
||||||
window.open(item.url);
|
window.open(item.url);
|
||||||
}
|
}
|
||||||
@ -446,18 +457,21 @@ function update() {
|
|||||||
const check = list.value.find((e) => !e.url);
|
const check = list.value.find((e) => !e.url);
|
||||||
|
|
||||||
if (!check) {
|
if (!check) {
|
||||||
emit("update:modelValue", list.value.map((e: Item) => e.url).join(","));
|
emit(
|
||||||
|
"update:modelValue",
|
||||||
|
list.value.map((e) => e.url.replace(/,/g, encodeURIComponent(","))).join(",")
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取
|
// 获取
|
||||||
watch(
|
watch(
|
||||||
() => props.modelValue,
|
() => props.modelValue,
|
||||||
(val: any) => {
|
(val: any[] | string) => {
|
||||||
const arr = (isArray(val) ? val : (val || "").split(",")).filter(Boolean);
|
const arr = (isArray(val) ? val : (val || "").split(",")).filter(Boolean);
|
||||||
|
|
||||||
list.value = arr
|
list.value = arr
|
||||||
.map((url: string) => {
|
.map((url) => {
|
||||||
const item = list.value.find((e) => url == e.url);
|
const item = list.value.find((e) => url == e.url);
|
||||||
|
|
||||||
if (item) {
|
if (item) {
|
||||||
@ -472,7 +486,7 @@ watch(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.filter((_: any, i: number) => {
|
.filter((_, i) => {
|
||||||
return props.multiple ? true : i == 0;
|
return props.multiple ? true : i == 0;
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -53,9 +53,7 @@ export default (): UserConfig => {
|
|||||||
assetFileNames: "static/[ext]/[name]-[hash].[ext]",
|
assetFileNames: "static/[ext]/[name]-[hash].[ext]",
|
||||||
manualChunks(id) {
|
manualChunks(id) {
|
||||||
if (id.includes("node_modules")) {
|
if (id.includes("node_modules")) {
|
||||||
if (
|
if (!["@cool-vue/crud"].find((e) => id.includes(e))) {
|
||||||
!["@cool-vue/crud", "@cool-vue/admin"].find((e) => id.includes(e))
|
|
||||||
) {
|
|
||||||
let str = id.toString().split("node_modules/")[1];
|
let str = id.toString().split("node_modules/")[1];
|
||||||
|
|
||||||
if (str[0] == "@") {
|
if (str[0] == "@") {
|
||||||
|
Loading…
Reference in New Issue
Block a user