diff --git a/.eslintrc.js b/.eslintrc.js index df549f7..783015b 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -58,19 +58,7 @@ module.exports = { "vue/multiline-html-element-content-newline": "off", "vue/singleline-html-element-content-newline": "off", "vue/attribute-hyphenation": "off", - // "vue/html-self-closing": "off", - "vue/require-default-prop": "off", - "vue/html-self-closing": [ - "error", - { - html: { - void: "always", - normal: "never", - component: "always" - }, - svg: "always", - math: "always" - } - ] + "vue/html-self-closing": "off", + "vue/require-default-prop": "off" } }; diff --git a/.vscode/crud.code-snippets b/.vscode/crud.code-snippets index 9bf91e3..55de878 100644 --- a/.vscode/crud.code-snippets +++ b/.vscode/crud.code-snippets @@ -3,8 +3,8 @@ "prefix": "cl-crud-ts", "body": [ "", "", - "", "" ], diff --git a/build/cool/index.ts b/build/cool/index.ts new file mode 100644 index 0000000..ce29a7a --- /dev/null +++ b/build/cool/index.ts @@ -0,0 +1,65 @@ +import { Plugin } from "vite"; +import { parseJson } from "./utils"; +import { getModules } from "./lib/modules"; +import { createEps, getEps } from "./lib/eps"; +import { createMenu } from "./lib/menu"; + +export const cool = (): Plugin | null => { + return { + name: "vite-cool", + configureServer(server) { + server.middlewares.use(async (req, res, next) => { + function done(data: any) { + res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" }); + res.end(JSON.stringify(data)); + } + + // 自定义 + if (req.url.includes("__cool")) { + const body = await parseJson(req); + let next: any = null; + + switch (req.url) { + // 快速创建菜单 + case "/__cool_createMenu": + next = createMenu(body); + break; + + // 获取模块列表 + case "/__cool_modules": + next = getModules(); + break; + + // 创建描述文件 + case "/__cool_eps": + next = createEps(body); + break; + } + + if (next) { + next.then((data: any) => { + done({ + code: 1000, + data + }); + }).catch((message: string) => { + done({ + code: 1001, + message + }); + }); + } + } else { + next(); + } + }); + }, + config() { + return { + define: { + __EPS__: getEps() + } + }; + } + }; +}; diff --git a/build/cool/lib/eps/index.ts b/build/cool/lib/eps/index.ts new file mode 100644 index 0000000..9d995f9 --- /dev/null +++ b/build/cool/lib/eps/index.ts @@ -0,0 +1,219 @@ +import prettier from "prettier"; +import { isEmpty, last } from "lodash"; +import { createDir, firstUpperCase, readFile, toCamel } from "../../utils"; +import { createWriteStream } from "fs"; +import { join } from "path"; + +// 临时目录路径 +const tempPath = join(__dirname, "../../temp"); + +// 创建描述文件 +export async function createEps({ list, service }: any) { + const t0 = [ + [ + ` + declare interface Crud { + /** + * 新增 + * @returns Promise + */ + add(data: any): Promise; + /** + * 删除 + * @returns Promise + */ + delete(data: { ids?: number[] | string[]; [key: string]: any }): Promise; + /** + * 修改 + * @returns Promise + */ + update(data: { id?: number | string; [key: string]: any }): Promise; + /** + * 详情 + * @returns Promise + */ + info(data: { id?: number | string; [key: string]: any }): Promise; + /** + * 全部 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 分页 + * @returns Promise + */ + page(data?: { page?: number | string; size?: number | string; [key: string]: any }): Promise; + } + `, + + ` + declare interface PageResponse { + list: any[]; + pagination: { size: number; page: number; total: number }; + [key: string]: any; + } + `, + + ` + declare interface RequestOptions { + params?: any; + data?: any; + url: string; + method?: "GET" | "get" | "POST" | "post" | string; + [key: string]: any; + } + ` + ] + ]; + + const t1 = [`declare type Service = {`, `request(data: RequestOptions): Promise;`]; + + // 处理数据 + function deep(d: any, k?: string) { + if (!k) k = ""; + + for (const i in d) { + const name = k + toCamel(firstUpperCase(i.replace(/[:]/g, ""))); + + if (d[i].namespace) { + // 查找配置 + const item = list.find((e: any) => (e.prefix || "").includes(d[i].namespace)); + + if (item) { + const t = [ + `declare interface ${name} ${item.extendCrud ? " extends Crud" : ""} {` + ]; + + t1.push(`${i}: ${name};`); + + // 插入方法 + if (item.api) { + // 权限列表 + const permission: string[] = []; + + item.api.forEach((a: any) => { + // 方法名 + const n = toCamel(a.name || last(a.path.split("/"))).replace( + /[:\/-]/g, + "" + ); + + if (n) { + // 参数类型 + let q: string[] = []; + + // 参数列表 + const { parameters = [] } = a.dts || {}; + + parameters.forEach((p: any) => { + if (p.description) { + q.push(`\n/** ${p.description} */\n`); + } + + if (p.name.includes(":")) { + return false; + } + + const a = `${p.name}${p.required ? "" : "?"}`; + const b = `${p.schema.type || "string"}`; + + q.push(`${a}: ${b},`); + }); + + if (isEmpty(q)) { + q = ["any"]; + } else { + q.unshift("{"); + q.push("}"); + } + + // 返回类型 + let res = ""; + + switch (a.path) { + case "/page": + res = "PageResponse"; + break; + default: + res = "any"; + break; + } + + // 描述 + t.push("\n"); + t.push("/**\n"); + t.push(` * ${a.summary || n}\n`); + t.push(` * @returns Promise<${res}>\n`); + t.push(" */\n"); + + t.push( + `${n}(data${q.length == 1 ? "?" : ""}: ${q.join( + "" + )}): Promise<${res}>;` + ); + } + + permission.push(`${n}: string;`); + }); + + // 添加权限 + t.push("\n"); + t.push("/**\n"); + t.push(" * 权限\n"); + t.push(" */\n"); + t.push(`permission: { ${permission.join("\n")} }`); + } + + t.push("}"); + t0.push(t); + } + } else { + t1.push(`${i}: {`); + deep(d[i], name); + t1.push(`},`); + } + } + } + + deep(service); + t1.push("}"); + + // 追加 + t0.push(t1); + + // 文本内容 + const content = prettier.format(t0.map((e) => e.join("")).join("\n\n"), { + parser: "typescript", + useTabs: true, + tabWidth: 4, + endOfLine: "lf", + semi: true, + singleQuote: false, + printWidth: 100, + trailingComma: "none" + }); + + // 创建 temp 目录 + createDir(tempPath); + + // 创建 service 描述文件 + createWriteStream(join(tempPath, "service.d.ts"), { + flags: "w" + }).write(content); + + // 创建 eps 文件 + createWriteStream(join(tempPath, "eps.json"), { + flags: "w" + }).write( + JSON.stringify( + list.map((e: any) => { + return [e.prefix, e.api.map((a: any) => [a.method || "", a.path, a.name || ""])]; + }) + ) + ); +} + +// 获取描述 +export function getEps() { + return JSON.stringify(readFile(join(tempPath, "eps.json"))); +} diff --git a/build/plugins/cool.ts b/build/cool/lib/menu/index.ts similarity index 81% rename from build/plugins/cool.ts rename to build/cool/lib/menu/index.ts index 5efc6df..07eefa0 100644 --- a/build/plugins/cool.ts +++ b/build/cool/lib/menu/index.ts @@ -1,12 +1,9 @@ -import { Plugin } from "vite"; +import { createWriteStream } from "fs"; import prettier from "prettier"; -import fs from "fs"; -import path from "path"; +import { join } from "path"; +import { createDir } from "../../utils"; +import rules from "./rules"; import { isFunction, isRegExp, isString } from "lodash"; -import rules from "../config/rules"; - -// 根路径 -const coolPath = path.join(__dirname, `../../src/cool`); // 格式化 function format(data: any) { @@ -107,23 +104,6 @@ const handler = { } }; -// 解析body -function parseJson(req: any) { - return new Promise((resolve, reject) => { - let d = ""; - req.on("data", function (chunk: Buffer) { - d += chunk; - }); - req.on("end", function () { - try { - resolve(JSON.parse(d)); - } catch (e) { - reject(e); - } - }); - }); -} - // 创建组件 function createComponent(item: any) { const { propertyName: prop, comment: label } = item; @@ -218,7 +198,7 @@ function datetimeMerge({ columns, item }: any) { } // 创建文件 -function createVue({ router, columns, prefix, api, module, filename }: any): void { +export async function createMenu({ router, columns, prefix, api, module, filename }: any): void { const upsert: any = { items: [] }; @@ -352,7 +332,7 @@ export default defineComponent({ : "" } - // 表格配置 + // cl-table 配置 const table = reactive(${JSON.stringify(table)}); // crud 加载 @@ -385,48 +365,13 @@ export default defineComponent({ }); // views 目录是否存在 - const dir = path.join(coolPath, `modules/${module}/views`); - if (!fs.existsSync(dir)) fs.mkdirSync(dir); + const dir = join(__dirname, `../../src/modules/${module}/views`); + + // 创建目录 + createDir(dir); // 创建文件 - fs.createWriteStream(path.join(dir, `${filename}.vue`), { + createWriteStream(join(dir, `${filename}.vue`), { flags: "w" }).write(content); } - -export const cool = (): Plugin | null => { - return { - name: "vite-cool", - configureServer(server) { - server.middlewares.use(async (req, res, next) => { - function done(data) { - res.writeHead(200, { "Content-Type": "text/html;charset=UTF-8" }); - res.end(JSON.stringify(data)); - } - - if (req.url.includes("/__cool_createMenu")) { - try { - const body: any = await parseJson(req); - await createVue(body); - done({ - code: 1000 - }); - } catch (e) { - done({ - code: 1001, - message: e.message - }); - } - } else if (req.url.includes("/__cool_modules")) { - const dirs = fs.readdirSync(path.join(coolPath, "modules")); - done({ - code: 1000, - data: dirs.filter((e) => !e.includes(".")) - }); - } else { - next(); - } - }); - } - }; -}; diff --git a/build/config/rules.ts b/build/cool/lib/menu/rules.ts similarity index 98% rename from build/config/rules.ts rename to build/cool/lib/menu/rules.ts index 47fedaf..0541686 100644 --- a/build/config/rules.ts +++ b/build/cool/lib/menu/rules.ts @@ -73,7 +73,7 @@ export default [ { test: ["date"], table: { - name: "cl-date", + name: "cl-date-text", props: { format: "YYYY-MM-DD" } @@ -89,7 +89,7 @@ export default [ { test: ["dates", "dateRange", "dateScope"], table: { - name: "cl-date", + name: "cl-date-text", props: { format: "YYYY-MM-DD" } diff --git a/build/cool/lib/modules/index.ts b/build/cool/lib/modules/index.ts new file mode 100644 index 0000000..b01d447 --- /dev/null +++ b/build/cool/lib/modules/index.ts @@ -0,0 +1,11 @@ +import fs from "fs"; +import { join } from "path"; + +export function getModules() { + try { + const dirs = fs.readdirSync(join(__dirname, "../../src/modules")); + return Promise.resolve(dirs.filter((e) => !e.includes("."))); + } catch (e) {} + + return ""; +} diff --git a/build/cool/temp/eps.json b/build/cool/temp/eps.json new file mode 100644 index 0000000..5c3df59 --- /dev/null +++ b/build/cool/temp/eps.json @@ -0,0 +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/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",""]]]] \ No newline at end of file diff --git a/build/cool/temp/service.d.ts b/build/cool/temp/service.d.ts new file mode 100644 index 0000000..b690274 --- /dev/null +++ b/build/cool/temp/service.d.ts @@ -0,0 +1,723 @@ +declare interface Crud { + /** + * 新增 + * @returns Promise + */ + add(data: any): Promise; + /** + * 删除 + * @returns Promise + */ + delete(data: { ids?: number[] | string[]; [key: string]: any }): Promise; + /** + * 修改 + * @returns Promise + */ + update(data: { id?: number | string; [key: string]: any }): Promise; + /** + * 详情 + * @returns Promise + */ + info(data: { id?: number | string; [key: string]: any }): Promise; + /** + * 全部 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 分页 + * @returns Promise + */ + page(data?: { + page?: number | string; + size?: number | string; + [key: string]: any; + }): Promise; +} + +declare interface PageResponse { + list: any[]; + pagination: { size: number; page: number; total: number }; + [key: string]: any; +} + +declare interface RequestOptions { + params?: any; + data?: any; + url: string; + method?: "GET" | "get" | "POST" | "post" | string; + [key: string]: any; +} + +declare interface BaseComm { + /** + * 修改个人信息 + * @returns Promise + */ + personUpdate(data?: any): Promise; + /** + * 文件上传模式 + * @returns Promise + */ + uploadMode(data?: any): Promise; + /** + * 权限与菜单 + * @returns Promise + */ + permmenu(data?: any): Promise; + /** + * 个人信息 + * @returns Promise + */ + person(data?: any): Promise; + /** + * 文件上传 + * @returns Promise + */ + upload(data?: any): Promise; + /** + * 退出 + * @returns Promise + */ + logout(data?: any): Promise; + /** + * list + * @returns Promise + */ + list(data?: any): Promise; + /** + * page + * @returns Promise + */ + page(data?: any): Promise; + /** + * info + * @returns Promise + */ + info(data?: any): Promise; + /** + * update + * @returns Promise + */ + update(data?: any): Promise; + /** + * delete + * @returns Promise + */ + delete(data?: any): Promise; + /** + * add + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + personUpdate: string; + uploadMode: string; + permmenu: string; + person: string; + upload: string; + logout: string; + list: string; + page: string; + info: string; + update: string; + delete: string; + add: string; + }; +} + +declare interface BaseOpen { + /** + * 刷新token + * @returns Promise + */ + refreshToken(data?: any): Promise; + /** + * 验证码 + * @returns Promise + */ + captcha(data?: any): Promise; + /** + * 登录 + * @returns Promise + */ + login(data?: any): Promise; + /** + * 获得网页内容的参数值 + * @returns Promise + */ + html(data?: any): Promise; + /** + * 实体信息与路径 + * @returns Promise + */ + eps(data?: any): Promise; + /** + * list + * @returns Promise + */ + list(data?: any): Promise; + /** + * page + * @returns Promise + */ + page(data?: any): Promise; + /** + * info + * @returns Promise + */ + info(data?: any): Promise; + /** + * update + * @returns Promise + */ + update(data?: any): Promise; + /** + * delete + * @returns Promise + */ + delete(data?: any): Promise; + /** + * add + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + refreshToken: string; + captcha: string; + login: string; + html: string; + eps: string; + list: string; + page: string; + info: string; + update: string; + delete: string; + add: string; + }; +} + +declare interface BaseSysDepartment { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 排序 + * @returns Promise + */ + order(data?: any): Promise; + /** + * 列表查询 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * page + * @returns Promise + */ + page(data?: any): Promise; + /** + * info + * @returns Promise + */ + info(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + order: string; + list: string; + add: string; + page: string; + info: string; + }; +} + +declare interface BaseSysLog { + /** + * 日志保存时间 + * @returns Promise + */ + setKeep(data?: any): Promise; + /** + * 获得日志保存时间 + * @returns Promise + */ + getKeep(data?: any): Promise; + /** + * 清理 + * @returns Promise + */ + clear(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * list + * @returns Promise + */ + list(data?: any): Promise; + /** + * info + * @returns Promise + */ + info(data?: any): Promise; + /** + * update + * @returns Promise + */ + update(data?: any): Promise; + /** + * delete + * @returns Promise + */ + delete(data?: any): Promise; + /** + * add + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + setKeep: string; + getKeep: string; + clear: string; + page: string; + list: string; + info: string; + update: string; + delete: string; + add: string; + }; +} + +declare interface BaseSysMenu { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 单个信息 + * @returns Promise + */ + info(data?: any): Promise; + /** + * 列表查询 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + info: string; + list: string; + page: string; + add: string; + }; +} + +declare interface BaseSysParam { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 获得网页内容的参数值 + * @returns Promise + */ + html(data?: any): Promise; + /** + * 单个信息 + * @returns Promise + */ + info(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * list + * @returns Promise + */ + list(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + html: string; + info: string; + page: string; + add: string; + list: string; + }; +} + +declare interface BaseSysRole { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 单个信息 + * @returns Promise + */ + info(data?: any): Promise; + /** + * 列表查询 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + info: string; + list: string; + page: string; + add: string; + }; +} + +declare interface BaseSysUser { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 移动部门 + * @returns Promise + */ + move(data?: any): Promise; + /** + * 单个信息 + * @returns Promise + */ + info(data?: any): Promise; + /** + * 列表查询 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + move: string; + info: string; + list: string; + page: string; + add: string; + }; +} + +declare interface DemoGoods { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 单个信息 + * @returns Promise + */ + info(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * 列表查询 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + info: string; + page: string; + list: string; + add: string; + }; +} + +declare interface SpaceInfo { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 单个信息 + * @returns Promise + */ + info(data?: any): Promise; + /** + * 列表查询 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + info: string; + list: string; + page: string; + add: string; + }; +} + +declare interface SpaceType { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 单个信息 + * @returns Promise + */ + info(data?: any): Promise; + /** + * 列表查询 + * @returns Promise + */ + list(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + info: string; + list: string; + page: string; + add: string; + }; +} + +declare interface TaskInfo { + /** + * 删除 + * @returns Promise + */ + delete(data?: any): Promise; + /** + * 修改 + * @returns Promise + */ + update(data?: any): Promise; + /** + * 开始 + * @returns Promise + */ + start(data?: any): Promise; + /** + * 执行一次 + * @returns Promise + */ + once(data?: any): Promise; + /** + * 停止 + * @returns Promise + */ + stop(data?: any): Promise; + /** + * 单个信息 + * @returns Promise + */ + info(data?: any): Promise; + /** + * 分页查询 + * @returns Promise + */ + page(data?: any): Promise; + /** + * 日志 + * @returns Promise + */ + log(data?: any): Promise; + /** + * 新增 + * @returns Promise + */ + add(data?: any): Promise; + /** + * list + * @returns Promise + */ + list(data?: any): Promise; + /** + * 权限 + */ + permission: { + delete: string; + update: string; + start: string; + once: string; + stop: string; + info: string; + page: string; + log: string; + add: string; + list: string; + }; +} + +declare type Service = { + request(data: RequestOptions): Promise; + base: { + comm: BaseComm; + open: BaseOpen; + sys: { + department: BaseSysDepartment; + log: BaseSysLog; + menu: BaseSysMenu; + param: BaseSysParam; + role: BaseSysRole; + user: BaseSysUser; + }; + }; + demo: { goods: DemoGoods }; + space: { info: SpaceInfo; type: SpaceType }; + task: { info: TaskInfo }; +}; diff --git a/build/cool/utils/index.ts b/build/cool/utils/index.ts new file mode 100644 index 0000000..9c1184e --- /dev/null +++ b/build/cool/utils/index.ts @@ -0,0 +1,46 @@ +import fs from "fs"; + +// 首字母大写 +export function firstUpperCase(value: string): string { + return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) { + return $1.toUpperCase() + $2; + }); +} + +// 横杠转驼峰 +export function toCamel(str: string): string { + return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) { + return $1 + $2.toUpperCase(); + }); +} + +// 创建目录 +export function createDir(path: string) { + if (!fs.existsSync(path)) fs.mkdirSync(path); +} + +// 读取文件 +export function readFile(name: string) { + try { + return fs.readFileSync(name, "utf8"); + } catch (e) {} + + return ""; +} + +// 解析body +export function parseJson(req: any) { + return new Promise((resolve) => { + let d = ""; + req.on("data", function (chunk: Buffer) { + d += chunk; + }); + req.on("end", function () { + try { + resolve(JSON.parse(d)); + } catch { + resolve({}); + } + }); + }); +} diff --git a/build/plugins/svg.ts b/build/svg/index.ts similarity index 100% rename from build/plugins/svg.ts rename to build/svg/index.ts diff --git a/package.json b/package.json index 155d3a7..77aa0d1 100644 --- a/package.json +++ b/package.json @@ -10,51 +10,57 @@ }, "dependencies": { "@cool-vue/crud": "^1.0.6", - "array.prototype.flat": "^1.2.4", - "axios": "^0.21.1", - "clipboard": "^2.0.8", + "@element-plus/icons-vue": "^1.1.3", + "@types/quill": "^2.0.9", + "axios": "^0.26.1", + "clipboard": "^2.0.10", "codemirror": "^5.62.0", "core-js": "^3.6.5", "echarts": "^5.0.2", - "element-plus": "^1.1.0-beta.20", + "element-plus": "^2.1.7", "file-saver": "^2.0.5", "js-beautify": "^1.13.5", - "mitt": "^2.1.0", + "lodash": "^4.17.21", + "mitt": "^3.0.0", "mockjs": "^1.1.0", "nprogress": "^0.2.0", + "pinia": "^2.0.12", "quill": "^1.3.7", - "socket.io-client": "^4.1.2", "store": "^2.0.12", "uuid": "^8.3.2", - "vue": "^3.2.20", - "vue-echarts": "^6.0.0-rc.3", - "vue-router": "^4.0.5", - "vuedraggable": "^4.0.1", - "vuex": "^4.0.0-0", + "vue": "^3.2.31", + "vue-echarts": "^6.0.2", + "vue-router": "^4.0.14", + "vuedraggable": "^4.1.0", "xlsx": "^0.16.9" }, "devDependencies": { "@types/lodash": "^4.14.168", "@types/node": "^16.10.2", + "@types/nprogress": "^0.2.0", + "@types/uuid": "^8.3.4", "@typescript-eslint/eslint-plugin": "^4.20.0", "@typescript-eslint/parser": "^4.20.0", - "@vitejs/plugin-vue": "1.9.2", - "@vitejs/plugin-vue-jsx": "^1.1.6", - "@vue/compiler-sfc": "3.2.19", + "@vitejs/plugin-vue": "^2.2.4", + "@vitejs/plugin-vue-jsx": "^1.3.8", + "@vue/cli-plugin-babel": "^5.0.1", + "@vue/cli-plugin-typescript": "^5.0.1", + "@vue/compiler-sfc": "^3.2.31", "@vue/composition-api": "^1.0.0-rc.13", "eslint": "^7.23.0", "eslint-config-prettier": "^8.1.0", "eslint-plugin-prettier": "^3.3.1", "eslint-plugin-vue": "^7.13.0", "iconv-lite": "^0.6.3", - "prettier": "^2.2.1", - "sass": "^1.42.1", + "prettier": "^2.4.1", + "sass": "^1.49.9", "sass-loader": "^11.1.1", "svg-sprite-loader": "^6.0.2", - "typescript": "4.4.3", - "unplugin-vue-components": "0.15.4", - "vite": "2.6.7", - "vite-plugin-compression": "^0.3.5", + "typescript": "^4.6.2", + "unplugin-vue-components": "^0.17.21", + "vite": "^2.8.6", + "vite-plugin-compression": "^0.5.1", + "vite-plugin-dts": "^0.9.9", "vite-plugin-mock": "^2.9.6", "vite-plugin-style-import": "^1.0.1", "vite-svg-loader": "^2.1.0" diff --git a/src/App.vue b/src/App.vue index acb0447..64a5fb3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,15 +1,15 @@ - - diff --git a/src/cool/modules/base/components/menu/slider/index.scss b/src/cool/modules/base/components/menu/slider/index.scss deleted file mode 100644 index c025100..0000000 --- a/src/cool/modules/base/components/menu/slider/index.scss +++ /dev/null @@ -1,90 +0,0 @@ -.cl-slider-menu { - height: 100%; - overflow-y: auto; - - &::-webkit-scrollbar { - width: 0; - height: 0; - } - - .el-menu { - border-right: 0; - background-color: transparent; - - .el-sub-menu__title, - &-item { - &.is-active, - &:hover { - background-color: $color-primary !important; - color: #fff; - } - } - - .el-sub-menu__title, - &-item, - &__title { - color: #eee; - letter-spacing: 0.5px; - height: 50px; - line-height: 50px; - - .icon-svg { - font-size: 16px; - margin: 0 15px 0 5px; - position: relative; - top: 1px; - } - - span { - font-size: 12px; - letter-spacing: 1px; - display: inline-block; - } - } - - &--collapse { - .el-sub-menu__title { - .icon-svg { - margin-left: 2px; - font-size: 19px; - } - } - } - } - - &__popup { - .icon-svg { - margin-right: 10px; - } - } -} - -.cl-slider-menu__submenu { - background-color: #fff; - - &.el-menu { - &--vertical { - .el-sub-menu { - &__title { - display: flex; - align-items: center; - - .icon-svg { - font-size: 18px; - margin-right: 10px; - } - } - } - - .el-menu-item { - display: flex; - align-items: center; - - .icon-svg { - font-size: 18px; - margin-right: 10px; - } - } - } - } -} diff --git a/src/cool/modules/base/components/menu/slider/index.tsx b/src/cool/modules/base/components/menu/slider/index.tsx deleted file mode 100644 index 5d66257..0000000 --- a/src/cool/modules/base/components/menu/slider/index.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import { computed, defineComponent, h, ref, watch } from "vue"; -import "./index.scss"; -import { useCool } from "/@/cool"; - -export default defineComponent({ - name: "cl-menu-slider", - - setup() { - const { router, route, store } = useCool(); - - // 是否可见 - const visible = ref(true); - // 菜单列表 - const menuList = computed(() => store.getters.menuList); - // 菜单是否折叠 - const menuCollapse = computed(() => store.getters.menuCollapse); - // 浏览器信息 - const browser: any = computed(() => store.getters.browser); - - // 页面跳转 - function toView(url: string) { - if (url != route.path) { - router.push(url); - } - } - - // 刷新菜单 - function refresh() { - visible.value = false; - - setTimeout(() => { - visible.value = true; - }, 0); - } - - // 监听菜单变化 - watch(menuList, refresh); - - return { - route, - visible, - menuList, - menuCollapse, - browser, - toView, - refresh - }; - }, - - render(ctx: any) { - function deepMenu(list: any, index: number) { - return list - .filter((e: any) => e.isShow) - .map((e: any) => { - let html = null; - - if (e.type == 0) { - html = h( - , - { - index: String(e.id), - key: e.id, - "popper-class": "cl-slider-menu__popup" - }, - { - title: () => { - return ctx.menuCollapse && index == 1 ? ( - - ) : ( - - - {e.name} - - ); - }, - default() { - return deepMenu(e.children, index + 1); - } - } - ); - } else { - html = h( - , - { - index: e.path, - key: e.id - }, - { - title() { - return {e.name}; - }, - default() { - return ; - } - } - ); - } - - return html; - }); - } - - const children = deepMenu(ctx.menuList, 1); - - return ( - ctx.visible && ( -
- - {children} - -
- ) - ); - } -}); diff --git a/src/cool/modules/base/components/process/index.vue b/src/cool/modules/base/components/process/index.vue deleted file mode 100644 index 2e8b047..0000000 --- a/src/cool/modules/base/components/process/index.vue +++ /dev/null @@ -1,255 +0,0 @@ - - - - - diff --git a/src/cool/modules/base/components/role/perms.vue b/src/cool/modules/base/components/role/perms.vue deleted file mode 100644 index 265e694..0000000 --- a/src/cool/modules/base/components/role/perms.vue +++ /dev/null @@ -1,151 +0,0 @@ - - - - - diff --git a/src/cool/modules/base/components/role/select.vue b/src/cool/modules/base/components/role/select.vue deleted file mode 100644 index 9f22901..0000000 --- a/src/cool/modules/base/components/role/select.vue +++ /dev/null @@ -1,59 +0,0 @@ - - - diff --git a/src/cool/modules/base/components/switch/index.vue b/src/cool/modules/base/components/switch/index.vue deleted file mode 100644 index 653e4e9..0000000 --- a/src/cool/modules/base/components/switch/index.vue +++ /dev/null @@ -1,112 +0,0 @@ - - - diff --git a/src/cool/modules/base/directives/permission.ts b/src/cool/modules/base/directives/permission.ts deleted file mode 100644 index 12cafc0..0000000 --- a/src/cool/modules/base/directives/permission.ts +++ /dev/null @@ -1,43 +0,0 @@ -import store from "/@/store"; - -function parse(value: any) { - const permission = store.getters.permission; - - if (typeof value == "string") { - return value ? permission.some((e: any) => e.includes(value.replace(/\s/g, ""))) : false; - } else { - return Boolean(value); - } -} - -function checkPerm(value: any) { - if (!value) { - return false; - } - - if (Object.prototype.toString.call(value) === "[object Object]") { - if (value.or) { - return value.or.some(parse); - } - - if (value.and) { - return value.and.some((e: any) => !parse(e)) ? false : true; - } - } - - return parse(value); -} - -function change(el: any, binding: any) { - el.style.display = checkPerm(binding.value) ? el.getAttribute("_display") : "none"; -} - -export default { - beforeMount(el: any, binding: any) { - el.setAttribute("_display", el.style.display || ""); - change(el, binding); - }, - updated: change -}; - -export { checkPerm }; diff --git a/src/cool/modules/base/index.ts b/src/cool/modules/base/index.ts deleted file mode 100644 index 1364405..0000000 --- a/src/cool/modules/base/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { checkPerm } from "./directives/permission"; -import { iconList } from "./common"; -import "./static/css/index.scss"; - -export { iconList, checkPerm }; diff --git a/src/cool/modules/base/pages/login/index.vue b/src/cool/modules/base/pages/login/index.vue deleted file mode 100644 index 8b1983e..0000000 --- a/src/cool/modules/base/pages/login/index.vue +++ /dev/null @@ -1,224 +0,0 @@ - - - - - diff --git a/src/cool/modules/base/service/common.ts b/src/cool/modules/base/service/common.ts deleted file mode 100644 index ab18a87..0000000 --- a/src/cool/modules/base/service/common.ts +++ /dev/null @@ -1,89 +0,0 @@ -import { BaseService, Service } from "/@/cool"; - -@Service("base/comm") -class Common extends BaseService { - /** - * 文件上传模式 - */ - uploadMode() { - return this.request({ - url: "/uploadMode" - }); - } - - /** - * 文件上传,如果模式是 cloud,返回对应参数 - * - * @returns - * @memberof CommonService - */ - upload(params: any) { - return this.request({ - url: "/upload", - method: "POST", - params - }); - } - - /** - * 用户退出 - */ - userLogout() { - return this.request({ - url: "/logout", - method: "POST" - }); - } - - /** - * 用户信息 - * - * @returns - * @memberof CommonService - */ - userInfo() { - return this.request({ - url: "/person" - }); - } - - /** - * 用户信息修改 - * - * @param {*} params - * @returns - * @memberof CommonService - */ - userUpdate(params: any) { - return this.request({ - url: "/personUpdate", - method: "POST", - data: { - ...params - } - }); - } - - /** - * 权限信息 - * - * @returns - * @memberof CommonService - */ - permMenu() { - return this.request({ - url: "/permmenu" - }); - } - - /** - * 数据接口 - */ - eps() { - return this.request({ - url: "/eps" - }); - } -} - -export default Common; diff --git a/src/cool/modules/base/service/open.ts b/src/cool/modules/base/service/open.ts deleted file mode 100644 index f19896a..0000000 --- a/src/cool/modules/base/service/open.ts +++ /dev/null @@ -1,56 +0,0 @@ -import { BaseService, Service } from "/@/cool"; - -@Service("base/open") -class Open extends BaseService { - /** - * 用户登录 - * - * @param {*} { username, password, captchaId, verifyCode } - * @returns - * @memberof CommonService - */ - userLogin({ username, password, captchaId, verifyCode }: any) { - return this.request({ - url: "/login", - method: "POST", - data: { - username, - password, - captchaId, - verifyCode - } - }); - } - - /** - * 图片验证码 svg - * - * @param {*} { height, width } - * @returns - * @memberof CommonService - */ - captcha({ height, width }: any) { - return this.request({ - url: "/captcha", - params: { - height, - width - } - }); - } - - /** - * 刷新 token - * @param {string} token - */ - refreshToken(token: string) { - return this.request({ - url: "/refreshToken", - params: { - refreshToken: token - } - }); - } -} - -export default Open; diff --git a/src/cool/modules/base/store/app.ts b/src/cool/modules/base/store/app.ts deleted file mode 100644 index c136777..0000000 --- a/src/cool/modules/base/store/app.ts +++ /dev/null @@ -1,78 +0,0 @@ -import store from "store"; -import { deepMerge, getBrowser } from "/@/cool/utils"; -import { app } from "/@/config/env"; -import { useEps } from "/@/cool"; - -const browser = getBrowser(); - -const state = { - info: { - ...app - }, - browser, - collapse: browser.isMini ? true : false, - loading: false -}; - -const getters = { - // 程序加载 - appLoading: (state: any) => state.loading, - // 应用配置 - app: (state: any) => state.info, - // 浏览器信息 - browser: (state: any) => state.browser, - // 左侧菜单是否收起 - menuCollapse: (state: any) => state.collapse -}; - -const actions = { - async appLoad({ getters, dispatch, commit }: any) { - if (getters.token) { - commit("SHOW_LOADING"); - - // 读取Eps - await useEps(); - - // 读取菜单权限 - await dispatch("permMenu"); - - // 获取用户信息 - dispatch("userInfo"); - - commit("HIDE_LOADING"); - } - } -}; - -const mutations = { - SHOW_LOADING(state: any) { - state.loading = true; - }, - - HIDE_LOADING(state: any) { - state.loading = false; - }, - - // 设置浏览器信息 - SET_BROWSER(state: any) { - state.browser = getBrowser(); - }, - - // 收起左侧菜单 - COLLAPSE_MENU(state: any, val = false) { - state.collapse = val; - }, - - // 更新应用配置 - UPDATE_APP(state: any, val: any) { - deepMerge(state.info, val); - store.set("__app__", state.info); - } -}; - -export default { - state, - getters, - actions, - mutations -}; diff --git a/src/cool/modules/base/store/menu.ts b/src/cool/modules/base/store/menu.ts deleted file mode 100644 index a98176d..0000000 --- a/src/cool/modules/base/store/menu.ts +++ /dev/null @@ -1,154 +0,0 @@ -import { ElMessage } from "element-plus"; -import storage from "store"; -import store from "/@/store"; -import router from "/@/router"; -import { deepTree, revDeepTree, isArray, isEmpty } from "/@/cool/utils"; -import { menuList } from "/@/config/env"; -import { revisePath } from "../utils"; -import { MenuItem } from "../types"; -import { usePermission } from "/@/cool"; - -const state = { - // 视图路由,type=1 - routes: storage.get("viewRoutes") || [], - // 树形菜单 - group: storage.get("menuGroup") || [], - // showAMenu 模式下,顶级菜单的序号 - index: 0, - // 左侧菜单 - menu: [], - // 权限列表 - permission: storage.get("permission") || [] -}; - -const getters = { - // 树形菜单列表 - menuGroup: (state: any) => state.group, - // 左侧菜单 - menuList: (state: any) => state.menu, - // 视图路由 - routes: (state: any) => state.routes, - // 权限列表 - permission: (state: any) => state.permission -}; - -const actions = { - // 设置菜单、权限 - permMenu({ commit, state, getters }: any) { - return new Promise((resolve, reject) => { - const next = (res: any) => { - if (!isArray(res.menus)) { - res.menus = []; - } - - if (!isArray(res.perms)) { - res.perms = []; - } - - const routes = res.menus - .filter((e: MenuItem) => e.type != 2) - .map((e: MenuItem) => { - return { - id: e.id, - parentId: e.parentId, - path: revisePath(e.router || String(e.id)), - viewPath: e.viewPath, - type: e.type, - name: e.name, - icon: e.icon, - orderNum: e.orderNum, - isShow: isEmpty(e.isShow) ? true : e.isShow, - meta: { - label: e.name, - keepAlive: e.keepAlive - }, - children: [] - }; - }); - - // 转成树形菜单 - const menuGroup = deepTree(routes); - - // 设置权限 - commit("SET_PERMIESSION", res.perms); - // 设置菜单组 - commit("SET_MENU_GROUP", menuGroup); - // 设置视图路由 - commit( - "SET_VIEW_ROUTES", - routes.filter((e: MenuItem) => e.type == 1) - ); - // 设置菜单 - commit("SET_MENU_LIST", state.index); - - resolve(menuGroup); - }; - - // 监测自定义菜单 - if (!getters.app.conf.customMenu) { - store.service.base.common - .permMenu() - .then((res: any) => { - next(res); - }) - .catch((err: string) => { - ElMessage.error("菜单加载异常"); - console.error(err); - reject(err); - }); - } else { - next({ - menus: revDeepTree(menuList) - }); - } - }); - } -}; - -const mutations = { - // 设置树形菜单列表 - SET_MENU_GROUP(state: any, list: MenuItem[]) { - state.group = list; - storage.set("menuGroup", list); - }, - - // 设置视图路由 - SET_VIEW_ROUTES(state: any, list: MenuItem[]) { - router.$plugin?.addViews(list); - - state.routes = list; - storage.set("viewRoutes", list); - }, - - // 设置左侧菜单 - SET_MENU_LIST(state: any, index: number) { - const { showAMenu } = store.getters.app.conf; - - if (isEmpty(index)) { - index = state.index; - } - - if (showAMenu) { - const { children = [] } = state.group[index] || {}; - - state.index = index; - state.menu = children; - } else { - state.menu = state.group; - } - }, - - // 设置权限 - SET_PERMIESSION(state: any, list: Array) { - state.permission = list; - storage.set("permission", list); - usePermission(list); - } -}; - -export default { - state, - getters, - actions, - mutations -}; diff --git a/src/cool/modules/base/store/module.ts b/src/cool/modules/base/store/module.ts deleted file mode 100644 index 4b5d1ac..0000000 --- a/src/cool/modules/base/store/module.ts +++ /dev/null @@ -1,30 +0,0 @@ -const state = { - info: {}, - list: [] -}; - -const getters = { - // 模块信息 - modules: (state: any) => state.info, - // 模块列表 - moduleList: (state: any) => state.list -}; - -const mutations = { - SET_MODULE(state: any, list: Array) { - const d: any = {}; - - list.forEach((e: any) => { - d[e.name] = e; - }); - - state.list = list; - state.info = d; - } -}; - -export default { - state, - getters, - mutations -}; diff --git a/src/cool/modules/base/store/process.ts b/src/cool/modules/base/store/process.ts deleted file mode 100644 index 4511e48..0000000 --- a/src/cool/modules/base/store/process.ts +++ /dev/null @@ -1,66 +0,0 @@ -const fMenu = { - label: "首页", - value: "/", - active: true -}; - -const state = { - list: [fMenu] -}; - -const getters = { - // 页面进程列表 - processList: (state: any) => state.list -}; - -const actions = {}; - -const mutations = { - ADD_PROCESS(state: any, item: any) { - const index = state.list.findIndex( - (e: any) => e.value.split("?")[0] === item.value.split("?")[0] - ); - - state.list.map((e: any) => { - e.active = e.value == item.value; - }); - - if (index < 0) { - if (item.value == "/") { - item.label = fMenu.label; - } - - if (item.label) { - state.list.push({ - ...item, - active: true - }); - } - } else { - state.list[index].active = true; - state.list[index].label = item.label; - state.list[index].value = item.value; - } - }, - - DEL_PROCESS(state: any, index: number) { - if (index != 0) { - state.list.splice(index, 1); - } - }, - - SET_PROCESS(state: any, list: Array) { - state.list = list; - }, - - RESET_PROCESS(state: any) { - state.list = [fMenu]; - } -}; - -export default { - state, - getters, - actions, - mutations -}; diff --git a/src/cool/modules/base/store/user.ts b/src/cool/modules/base/store/user.ts deleted file mode 100644 index b14edc2..0000000 --- a/src/cool/modules/base/store/user.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { storage, href } from "/@/cool/utils"; -import store from "/@/store"; -import { Token } from "../types"; - -const state: any = { - // 授权标识 - token: storage.get("token") || null, - // 用户信息 - info: storage.get("userInfo") || {} -}; - -const getters = { - userInfo: (state: any) => state.info, - token: (state: any) => state.token -}; - -const actions = { - // 用户登录 - userLogin({ commit }: any, form: any): Promise { - return store.service.base.open.userLogin(form).then((res: Token) => { - commit("SET_TOKEN", res); - return res; - }); - }, - - // 用户退出 - async userLogout({ dispatch }: any): Promise { - await store.service.base.common.userLogout(); - return dispatch("userRemove"); - }, - - // 用户信息 - userInfo({ commit }: any): Promise { - return store.service.base.common.userInfo().then((res: any) => { - commit("SET_USERINFO", res); - return res; - }); - }, - - // 用户移除 - userRemove({ commit }: any) { - commit("CLEAR_USER"); - commit("CLEAR_TOKEN"); - commit("RESET_PROCESS"); - commit("SET_MENU_GROUP", []); - commit("SET_VIEW_ROUTES", []); - commit("SET_MENU_LIST", 0); - }, - - // 刷新token - refreshToken({ commit, dispatch }: any) { - return new Promise((resolve, reject) => { - store.service.base.open - .refreshToken(storage.get("refreshToken")) - .then((res: any) => { - commit("SET_TOKEN", res); - resolve(res.token); - }) - .catch((err: Error) => { - dispatch("userRemove"); - href("/login"); - reject(err); - }); - }); - } -}; - -const mutations = { - // 设置用户信息 - SET_USERINFO(state: any, val: any) { - state.info = val; - storage.set("userInfo", val); - }, - - // 设置授权标识 - SET_TOKEN(state: any, { token, expire, refreshToken, refreshExpire }: Token) { - // 请求的唯一标识 - state.token = token; - storage.set("token", token, expire); - - // 刷新 token 的唯一标识 - storage.set("refreshToken", refreshToken, refreshExpire); - }, - - // 移除授权标识 - CLEAR_TOKEN(state: any) { - state.token = null; - storage.remove("token"); - storage.remove("refreshToken"); - }, - - // 移除用户信息 - CLEAR_USER(state: any) { - state.info = {}; - storage.remove("userInfo"); - } -}; - -export default { - state, - getters, - actions, - mutations -}; diff --git a/src/cool/modules/base/types/index.d.ts b/src/cool/modules/base/types/index.d.ts deleted file mode 100644 index 9d32086..0000000 --- a/src/cool/modules/base/types/index.d.ts +++ /dev/null @@ -1,31 +0,0 @@ -export declare interface Token { - expire: number; - refreshExpire: number; - refreshToken: string; - token: string; -} - -export declare enum MenuType { - "目录" = 0, - "菜单" = 1, - "权限" = 2 -} - -export declare interface MenuItem { - id: number; - parentId: number; - path: string; - router?: string; - viewPath?: string; - type: MenuType; - name: string; - icon: string; - orderNum: number; - isShow: number; - keepAlive?: number; - meta?: { - label: string; - keepAlive: number; - }; - children?: MenuItem[]; -} diff --git a/src/cool/modules/base/views/log.vue b/src/cool/modules/base/views/log.vue deleted file mode 100644 index 4e66774..0000000 --- a/src/cool/modules/base/views/log.vue +++ /dev/null @@ -1,161 +0,0 @@ - - - diff --git a/src/cool/modules/base/views/menu.vue b/src/cool/modules/base/views/menu.vue deleted file mode 100644 index 6c72440..0000000 --- a/src/cool/modules/base/views/menu.vue +++ /dev/null @@ -1,398 +0,0 @@ - - - diff --git a/src/cool/modules/base/views/param.vue b/src/cool/modules/base/views/param.vue deleted file mode 100644 index bdc6b77..0000000 --- a/src/cool/modules/base/views/param.vue +++ /dev/null @@ -1,218 +0,0 @@ - - - - - diff --git a/src/cool/modules/base/views/plugin.vue b/src/cool/modules/base/views/plugin.vue deleted file mode 100644 index a96fb98..0000000 --- a/src/cool/modules/base/views/plugin.vue +++ /dev/null @@ -1,263 +0,0 @@ - - - diff --git a/src/cool/modules/base/views/role.vue b/src/cool/modules/base/views/role.vue deleted file mode 100644 index b8f90d8..0000000 --- a/src/cool/modules/base/views/role.vue +++ /dev/null @@ -1,171 +0,0 @@ - - - diff --git a/src/cool/modules/base/views/user.vue b/src/cool/modules/base/views/user.vue deleted file mode 100644 index 843550d..0000000 --- a/src/cool/modules/base/views/user.vue +++ /dev/null @@ -1,576 +0,0 @@ - - - - - diff --git a/src/cool/modules/chat/components/chat.vue b/src/cool/modules/chat/components/chat.vue deleted file mode 100644 index c87672f..0000000 --- a/src/cool/modules/chat/components/chat.vue +++ /dev/null @@ -1,288 +0,0 @@ - - - - - diff --git a/src/cool/modules/chat/components/emoji.vue b/src/cool/modules/chat/components/emoji.vue deleted file mode 100644 index 919134f..0000000 --- a/src/cool/modules/chat/components/emoji.vue +++ /dev/null @@ -1,210 +0,0 @@ - - - - - - - diff --git a/src/cool/modules/chat/components/icon-voice.vue b/src/cool/modules/chat/components/icon-voice.vue deleted file mode 100644 index 875b4ed..0000000 --- a/src/cool/modules/chat/components/icon-voice.vue +++ /dev/null @@ -1,45 +0,0 @@ - - - - - diff --git a/src/cool/modules/chat/components/input.vue b/src/cool/modules/chat/components/input.vue deleted file mode 100644 index 44e7005..0000000 --- a/src/cool/modules/chat/components/input.vue +++ /dev/null @@ -1,216 +0,0 @@ - - - - - diff --git a/src/cool/modules/chat/components/message.vue b/src/cool/modules/chat/components/message.vue deleted file mode 100644 index fe628bd..0000000 --- a/src/cool/modules/chat/components/message.vue +++ /dev/null @@ -1,546 +0,0 @@ - - - - - diff --git a/src/cool/modules/chat/components/notice.vue b/src/cool/modules/chat/components/notice.vue deleted file mode 100644 index 7b8c1ff..0000000 --- a/src/cool/modules/chat/components/notice.vue +++ /dev/null @@ -1,67 +0,0 @@ - - - - - diff --git a/src/cool/modules/chat/components/session.vue b/src/cool/modules/chat/components/session.vue deleted file mode 100644 index 8472e54..0000000 --- a/src/cool/modules/chat/components/session.vue +++ /dev/null @@ -1,316 +0,0 @@ - - - - - diff --git a/src/cool/modules/chat/components/upload.vue b/src/cool/modules/chat/components/upload.vue deleted file mode 100644 index 090917e..0000000 --- a/src/cool/modules/chat/components/upload.vue +++ /dev/null @@ -1,120 +0,0 @@ - - - diff --git a/src/cool/modules/chat/service/message.ts b/src/cool/modules/chat/service/message.ts deleted file mode 100644 index 0907cb6..0000000 --- a/src/cool/modules/chat/service/message.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { BaseService, Service, Permission } from "/@/cool"; - -@Service({ - namespace: "im/message", - mock: true -}) -class ImMessage extends BaseService { - @Permission("read") - read(data: any) { - return this.request({ - url: "/read", - method: "POST", - data - }); - } -} - -export default ImMessage; diff --git a/src/cool/modules/chat/service/session.ts b/src/cool/modules/chat/service/session.ts deleted file mode 100644 index 099b670..0000000 --- a/src/cool/modules/chat/service/session.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { BaseService, Service, Permission } from "/@/cool"; - -@Service({ - namespace: "im/session", - mock: true -}) -class ImSession extends BaseService { - @Permission("unreadCount") - unreadCount() { - return this.request({ - url: "/unreadCount" - }); - } -} - -export default ImSession; diff --git a/src/cool/modules/chat/static/images/custom-avatar.png b/src/cool/modules/chat/static/images/custom-avatar.png deleted file mode 100644 index d69c80f..0000000 Binary files a/src/cool/modules/chat/static/images/custom-avatar.png and /dev/null differ diff --git a/src/cool/modules/chat/static/images/emoji.png b/src/cool/modules/chat/static/images/emoji.png deleted file mode 100644 index b233094..0000000 Binary files a/src/cool/modules/chat/static/images/emoji.png and /dev/null differ diff --git a/src/cool/modules/chat/static/images/image.png b/src/cool/modules/chat/static/images/image.png deleted file mode 100644 index e119fa2..0000000 Binary files a/src/cool/modules/chat/static/images/image.png and /dev/null differ diff --git a/src/cool/modules/chat/static/images/video.png b/src/cool/modules/chat/static/images/video.png deleted file mode 100644 index 80dd1be..0000000 Binary files a/src/cool/modules/chat/static/images/video.png and /dev/null differ diff --git a/src/cool/modules/chat/static/notify.mp3 b/src/cool/modules/chat/static/notify.mp3 deleted file mode 100644 index 594a32b..0000000 Binary files a/src/cool/modules/chat/static/notify.mp3 and /dev/null differ diff --git a/src/cool/modules/chat/store/message.ts b/src/cool/modules/chat/store/message.ts deleted file mode 100644 index a81d00f..0000000 --- a/src/cool/modules/chat/store/message.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { isArray } from "/@/cool/utils"; - -const state = { - list: [] -}; - -const getters = { - messageList: (state: any) => state.list -}; - -const actions = {}; - -const mutations = { - // 设置列表 - SET_MESSAGE_LIST(state: any, data: any[]) { - state.list = data; - }, - - // 追加数据 - APPEND_MESSAGE_LIST(state: any, data: any) { - state.list.push(data); - }, - - // 追加数据到头部 - PREPEND_MESSAGE_LIST(state: any, data: any) { - const list = isArray(data) ? data : [data]; - state.list.unshift(...list.reverse()); - }, - - // 清空列表 - CLEAR_MESSAGE_LIST(state: any) { - state.list = []; - }, - - // 更新消息数据 - UPDATE_MESSAGE(state: any, { file, data, callback }: any) { - let item = null; - - if (file) { - item = state.list.find((e: any) => e.uid === file.uid); - } - - if (item) { - if (data) { - Object.assign(item, data); - } - - if (callback) callback(item); - } - } -}; - -export default { - state, - getters, - actions, - mutations -}; diff --git a/src/cool/modules/chat/store/session.ts b/src/cool/modules/chat/store/session.ts deleted file mode 100644 index 3447cb6..0000000 --- a/src/cool/modules/chat/store/session.ts +++ /dev/null @@ -1,63 +0,0 @@ -import { isBoolean } from "/@/cool/utils"; - -const state = { - list: [], - current: null, - visible: true -}; - -const getters = { - // 当前会话 - session: (state: any) => state.current, - // 会话列表 - sessionList: (state: any) => state.list, - // 是否显示会话列表 - sessionVisible: (state: any) => state.visible -}; - -const actions = {}; - -const mutations = { - // 设置会话信息 - SET_SESSION(state: any, data: any) { - state.current = data; - state.current.serviceUnreadCount = 0; - }, - - // 清空会话信息 - CLEAR_SESSION(state: any) { - state.session = null; - }, - - // 更新会话信息 - UPDATE_SESSION(state: any, data: any) { - Object.assign(state.current, data); - }, - - // 设置会话列表 - SET_SESSION_LIST(state: any, data: any[]) { - state.list = data; - }, - - // 清空会话列表 - CLEAR_SESSION_LIST(state: any) { - state.list = []; - }, - - // 打开会话列表 - OPEN_SESSION(state: any, val: any) { - state.visible = isBoolean(val) ? val : !state.visible; - }, - - // 关闭会话列表 - CLOSE_SESSION(state: any) { - state.visible = false; - } -}; - -export default { - state, - getters, - actions, - mutations -}; diff --git a/src/cool/modules/chat/utils/index.ts b/src/cool/modules/chat/utils/index.ts deleted file mode 100644 index 73e6bbb..0000000 --- a/src/cool/modules/chat/utils/index.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { isObject } from "/@/cool/utils"; - -export function parseContent({ content, contentType }: any) { - const data = isObject(content) ? content : JSON.parse(content); - let text = ""; - - switch (contentType) { - case 0: - text = data.text; - break; - case 1: - text = "[图片]"; - break; - case 2: - text = "[表情]"; - break; - case 3: - text = "[语音]"; - break; - case 4: - text = "[视频]"; - break; - case 5: - text = "[商品信息]"; - break; - } - - data._text = text; - - return data; -} diff --git a/src/cool/modules/demo/components/crud/adv-search.vue b/src/cool/modules/demo/components/crud/adv-search.vue deleted file mode 100644 index 0fbb0c3..0000000 --- a/src/cool/modules/demo/components/crud/adv-search.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - diff --git a/src/cool/modules/demo/components/crud/context-menu.vue b/src/cool/modules/demo/components/crud/context-menu.vue deleted file mode 100644 index b35b093..0000000 --- a/src/cool/modules/demo/components/crud/context-menu.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/src/cool/modules/demo/components/crud/dialog.vue b/src/cool/modules/demo/components/crud/dialog.vue deleted file mode 100644 index bf1b548..0000000 --- a/src/cool/modules/demo/components/crud/dialog.vue +++ /dev/null @@ -1,60 +0,0 @@ - - - - - diff --git a/src/cool/modules/demo/components/crud/form.vue b/src/cool/modules/demo/components/crud/form.vue deleted file mode 100644 index 10a45cc..0000000 --- a/src/cool/modules/demo/components/crud/form.vue +++ /dev/null @@ -1,316 +0,0 @@ - - - - - diff --git a/src/cool/modules/demo/components/crud/query.vue b/src/cool/modules/demo/components/crud/query.vue deleted file mode 100644 index 3dbada8..0000000 --- a/src/cool/modules/demo/components/crud/query.vue +++ /dev/null @@ -1,27 +0,0 @@ - - - diff --git a/src/cool/modules/demo/components/crud/render/test.vue b/src/cool/modules/demo/components/crud/render/test.vue deleted file mode 100644 index 71eac26..0000000 --- a/src/cool/modules/demo/components/crud/render/test.vue +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/src/cool/modules/demo/components/crud/render/test2.tsx b/src/cool/modules/demo/components/crud/render/test2.tsx deleted file mode 100644 index abab081..0000000 --- a/src/cool/modules/demo/components/crud/render/test2.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { defineComponent, ref } from "vue"; - -export default defineComponent({ - name: "test2", - - props: { - modelValue: String, - scope: null - }, - - emits: ["update:modelValue", "change"], - - setup(props, { emit }) { - const value = ref(props.modelValue || ""); - - function onChange(val: string) { - emit("update:modelValue", val); - emit("change", val); - } - - return { - value, - onChange - }; - }, - - render(ctx: any) { - return ; - } -}); diff --git a/src/cool/modules/demo/components/crud/table.vue b/src/cool/modules/demo/components/crud/table.vue deleted file mode 100644 index e26dad3..0000000 --- a/src/cool/modules/demo/components/crud/table.vue +++ /dev/null @@ -1,95 +0,0 @@ - - - - - diff --git a/src/cool/modules/demo/components/crud/upsert.vue b/src/cool/modules/demo/components/crud/upsert.vue deleted file mode 100644 index 389e114..0000000 --- a/src/cool/modules/demo/components/crud/upsert.vue +++ /dev/null @@ -1,77 +0,0 @@ - - - diff --git a/src/cool/modules/demo/components/demo/b-cl-context-menu.vue b/src/cool/modules/demo/components/demo/b-cl-context-menu.vue deleted file mode 100644 index a4bae2a..0000000 --- a/src/cool/modules/demo/components/demo/b-cl-context-menu.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/src/cool/modules/demo/components/demo/b-cl-crud.vue b/src/cool/modules/demo/components/demo/b-cl-crud.vue deleted file mode 100644 index fcc1352..0000000 --- a/src/cool/modules/demo/components/demo/b-cl-crud.vue +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/src/cool/modules/demo/components/demo/b-cl-editor-quill.vue b/src/cool/modules/demo/components/demo/b-cl-editor-quill.vue deleted file mode 100644 index 36f7166..0000000 --- a/src/cool/modules/demo/components/demo/b-cl-editor-quill.vue +++ /dev/null @@ -1,14 +0,0 @@ - diff --git a/src/cool/modules/demo/components/demo/b-cl-upload.vue b/src/cool/modules/demo/components/demo/b-cl-upload.vue deleted file mode 100644 index 502efbb..0000000 --- a/src/cool/modules/demo/components/demo/b-cl-upload.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - diff --git a/src/cool/modules/demo/components/demo/b-error-page.vue b/src/cool/modules/demo/components/demo/b-error-page.vue deleted file mode 100644 index 86ce9ac..0000000 --- a/src/cool/modules/demo/components/demo/b-error-page.vue +++ /dev/null @@ -1,29 +0,0 @@ - - - diff --git a/src/cool/modules/demo/components/demo/b-icon-svg.vue b/src/cool/modules/demo/components/demo/b-icon-svg.vue deleted file mode 100644 index 61fd63c..0000000 --- a/src/cool/modules/demo/components/demo/b-icon-svg.vue +++ /dev/null @@ -1,26 +0,0 @@ - - - diff --git a/src/cool/modules/demo/components/demo/b-v-copy.vue b/src/cool/modules/demo/components/demo/b-v-copy.vue deleted file mode 100644 index 3789ad9..0000000 --- a/src/cool/modules/demo/components/demo/b-v-copy.vue +++ /dev/null @@ -1,18 +0,0 @@ - diff --git a/src/cool/modules/demo/views/crud.vue b/src/cool/modules/demo/views/crud.vue deleted file mode 100644 index db820a2..0000000 --- a/src/cool/modules/demo/views/crud.vue +++ /dev/null @@ -1,78 +0,0 @@ - - - diff --git a/src/cool/modules/demo/views/demo.vue b/src/cool/modules/demo/views/demo.vue deleted file mode 100644 index ef923c8..0000000 --- a/src/cool/modules/demo/views/demo.vue +++ /dev/null @@ -1,118 +0,0 @@ - - - - - diff --git a/src/cool/modules/demo/views/upload.vue b/src/cool/modules/demo/views/upload.vue deleted file mode 100644 index 4d6e89b..0000000 --- a/src/cool/modules/demo/views/upload.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/src/cool/modules/upload/components/index.vue b/src/cool/modules/upload/components/index.vue deleted file mode 100644 index f4aef88..0000000 --- a/src/cool/modules/upload/components/index.vue +++ /dev/null @@ -1,676 +0,0 @@ - - - - - diff --git a/src/cool/modules/upload/components/space/category.vue b/src/cool/modules/upload/components/space/category.vue deleted file mode 100644 index 801d02a..0000000 --- a/src/cool/modules/upload/components/space/category.vue +++ /dev/null @@ -1,302 +0,0 @@ - - - - - diff --git a/src/cool/modules/upload/components/space/file-item.vue b/src/cool/modules/upload/components/space/file-item.vue deleted file mode 100644 index 4e4b378..0000000 --- a/src/cool/modules/upload/components/space/file-item.vue +++ /dev/null @@ -1,213 +0,0 @@ - - - - - diff --git a/src/cool/modules/upload/components/space/index.vue b/src/cool/modules/upload/components/space/index.vue deleted file mode 100644 index 5ac8beb..0000000 --- a/src/cool/modules/upload/components/space/index.vue +++ /dev/null @@ -1,505 +0,0 @@ - - - - - diff --git a/src/cool/modules/upload/config.ts b/src/cool/modules/upload/config.ts deleted file mode 100644 index 635de9d..0000000 --- a/src/cool/modules/upload/config.ts +++ /dev/null @@ -1,18 +0,0 @@ -export default { - // 上传的地址 - action: "", - // 上传的文件类型 - accept: "", - // 上传的文件字段名 - name: "file", - // 尺寸 - size: "128px", - // 显示图标 - icon: "el-icon-picture", - // 显示文案 - text: "选择文件", - // 上传大小限制 - limitSize: 2, - // 是否已 uuid 重新命名 - rename: true -}; diff --git a/src/cool/router/base.ts b/src/cool/router/base.ts new file mode 100644 index 0000000..3341bc7 --- /dev/null +++ b/src/cool/router/base.ts @@ -0,0 +1,89 @@ +import { ElMessage } from "element-plus"; +import { + createRouter, + createWebHashHistory, + createWebHistory, + NavigationGuardNext, + RouteRecordRaw +} from "vue-router"; +import { storage } from "/@/cool"; +import { useBaseStore } from "/$/base"; +import { routerMode } from "/@/cool/config"; + +// 忽略 +const ignore: any = { + token: ["/login", "/403", "/404", "/500", "/502"] +}; + +// 默认路由 +const routes: RouteRecordRaw[] = [ + { + path: "/", + name: "index", + component: () => import("/$/base/pages/layout/index.vue"), + children: [ + { + path: "/", + name: "数据统计", + component: () => import("/@/views/home/index.vue") + } + ] + }, + { + path: "/:catchAll(.*)", + name: "404", + redirect: "/404" + } +]; + +// 创建 +const router = createRouter({ + history: routerMode == "history" ? createWebHistory() : createWebHashHistory(), + routes +}); + +// 路由守卫 +router.beforeEach((to: any, _: any, next: NavigationGuardNext) => { + const { user, process } = useBaseStore(); + + if (user.token) { + if (to.path.includes("/login")) { + // 登录成功且 token 未过期,回到首页 + if (!storage.isExpired("token")) { + return next("/"); + } + } else { + // 添加路由进程 + process.add({ + keepAlive: to.meta?.keepAlive, + label: to.meta?.label || to.name, + value: to.fullPath + }); + } + } else { + if (!ignore.token.find((e: string) => to.path == e)) { + return next("/login"); + } + } + + next(); +}); + +let lock = false; + +// 错误监听 +router.onError((err: any) => { + if (!lock) { + lock = true; + + ElMessage.error("页面不存在或者未配置!"); + console.error(err); + + setTimeout(() => { + lock = false; + }, 0); + } +}); + +export { router }; +export * from "./views"; diff --git a/src/cool/router/index.ts b/src/cool/router/index.ts new file mode 100644 index 0000000..426ec23 --- /dev/null +++ b/src/cool/router/index.ts @@ -0,0 +1,2 @@ +export * from "./base"; +export * from "./views"; diff --git a/src/cool/router/views.ts b/src/cool/router/views.ts new file mode 100644 index 0000000..d34f90e --- /dev/null +++ b/src/cool/router/views.ts @@ -0,0 +1,47 @@ +import { cloneDeep, isArray } from "lodash"; +import { router } from "./base"; + +const views = import.meta.globEager("/src/**/views/**/*.vue"); + +for (const i in views) { + views[i.slice(5)] = views[i]; + delete views[i]; +} + +export function addViews(data: any[] | any) { + // 列表 + const list = isArray(data) ? data : [data]; + + list.forEach((e: any) => { + const d: any = cloneDeep(e); + + // 命名 + d.name = d.router; + + if (!d.component) { + const url = d.viewPath; + + if (url) { + if ( + /^(http[s]?:\/\/)([0-9a-z.]+)(:[0-9]+)?([/0-9a-z.]+)?(\?[0-9a-z&=]+)?(#[0-9-a-z]+)?/i.test( + url + ) + ) { + d.meta.iframeUrl = url; + d.component = () => import(`/$/base/pages/iframe/index.vue`); + } else { + d.component = () => Promise.resolve(views[url.replace("cool/", "")]); + } + } else { + d.redirect = "/404"; + } + } + + // 批量添加 + router.addRoute("index", d); + }); +} + +export function getViews() { + return router.getRoutes().find((e) => e.name == "index")?.children; +} diff --git a/src/cool/service/base.ts b/src/cool/service/base.ts new file mode 100644 index 0000000..24a0b38 --- /dev/null +++ b/src/cool/service/base.ts @@ -0,0 +1,127 @@ +// @ts-nocheck +import { baseUrl, isDev, test } from "../config"; +import { isObject } from "../utils"; +import request from "./request"; + +export function Service( + value: + | string + | { + namespace?: string; + url?: string; + mock?: boolean; + } +) { + return function (target: any) { + // 命名 + if (typeof value == "string") { + target.prototype.namespace = value; + } + + // 复杂项 + if (isObject(value)) { + target.prototype.namespace = value.namespace; + target.prototype.mock = value.mock; + + if (value.url) { + target.prototype.url = value.url; + } + } + }; +} + +export class BaseService { + constructor( + options = {} as { + namespace?: string; + } + ) { + if (options?.namespace) { + this.namespace = options.namespace; + } + } + + request( + options = {} as { + params?: any; + data?: any; + url: string; + method?: "GET" | "get" | "POST" | "post" | string; + [key: string]: any; + } + ) { + if (!options.params) options.params = {}; + + let ns = ""; + + // 是否 mock 模式 + if (this.mock || test.mock) { + // 测试 + } else { + if (isDev) { + ns = this.proxy || baseUrl; + } else { + ns = this.proxy ? this.url : baseUrl; + } + } + + // 拼接前缀 + if (this.namespace) { + ns += "/" + this.namespace; + } + + // 处理 http + if (options.url.indexOf("http") !== 0) { + options.url = ns + options.url; + } + + return request(options); + } + + list(data: any) { + return this.request({ + url: "/list", + method: "POST", + data + }); + } + + page(data: { page?: number; size?: number; [key: string]: any }) { + return this.request({ + url: "/page", + method: "POST", + data + }); + } + + info(params: { id?: number | string; [key: string]: any }) { + return this.request({ + url: "/info", + params + }); + } + + update(data: { id?: number | string; [key: string]: any }) { + return this.request({ + url: "/update", + method: "POST", + data + }); + } + + delete(data: { ids?: number[] | string[]; [key: string]: any }) { + return this.request({ + url: "/delete", + method: "POST", + data + }); + } + + add(data: any) { + return this.request({ + url: "/add", + method: "POST", + data + }); + } +} diff --git a/src/cool/service/eps.ts b/src/cool/service/eps.ts new file mode 100644 index 0000000..3832779 --- /dev/null +++ b/src/cool/service/eps.ts @@ -0,0 +1,204 @@ +import { isDev, test } from "../config"; +import { BaseService } from "./base"; +import { storage, toCamel } from "../utils"; + +// 获取标签名 +function getNames(v: any) { + return Object.getOwnPropertyNames(v.constructor.prototype).filter( + (e) => !["namespace", "constructor", "request"].includes(e) + ); +} + +// 标签名 +const names = getNames(new BaseService()); + +export function useEps(service: Service) { + // 创建描述文件 + function createDts(list: any[]) { + function deep(v: any) { + for (const i in v) { + if (v[i].namespace) { + v[i].namespace = v[i].namespace; + + // 模块 + const item: any = list.find((e: any) => e.prefix.includes(v[i].namespace)); + + // 接口 + const api: any[] = item ? item.api : []; + + // 获取方法集合 + [...names, ...getNames(v[i])].forEach((e) => { + if (!api.find((a) => a.path.includes(e))) { + api.push({ + path: `/${e}` + }); + } + }); + + if (item) { + item.api = api; + } else { + list.push({ + prefix: `/${v[i].namespace}`, + api + }); + } + } else { + deep(v[i]); + } + } + } + + deep(service); + + // 本地服务 + return service.request({ + url: `http://localhost:${__SERVER_PORT__}/__cool_eps`, + method: "POST", + data: { + service, + list + } + }); + } + + // 获取 eps + function getEps() { + if (isDev) { + service + .request({ + url: "/admin/base/open/eps" + }) + .then(async (res) => { + storage.set("eps", res); + set(res, true); + console.log("[Eps] 初始化成功。"); + }) + .catch((err) => { + console.error("[Eps] 获取失败!", err.message); + }); + } + } + + // 设置 + async function set(d: any, c?: boolean) { + // 接口列表 + const list: any[] = []; + + if (!d) { + return false; + } + + for (const i in d) { + d[i].forEach((e: any) => { + // 分隔路径 + const arr = e.prefix + .replace(/\//, "") + .replace("admin", "") + .split("/") + .filter(Boolean) + .map(toCamel); + + // 遍历 + function deep(d: any, i: number) { + const k = arr[i]; + + if (k) { + // 是否最后一个 + if (arr[i + 1]) { + if (!d[k]) { + d[k] = {}; + } + + deep(d[k], i + 1); + } else { + // 本地不存在则创建实例 + if (!d[k]) { + d[k] = new BaseService({ + namespace: e.prefix.substr(1, e.prefix.length - 1) + }); + } + + // 创建方法 + e.api.forEach((a: any) => { + // 方法名 + const n = (a.name || a.path).replace("/", ""); + + // 过滤 + if (!names.includes(n)) { + // 本地不存在则创建 + if (!d[k][n]) { + if (n && !/[-:]/g.test(n)) { + d[k][n] = function (data: any) { + return this.request({ + url: a.path, + method: a.method, + [a.method.toLocaleLowerCase() == "post" + ? "data" + : "params"]: data + }); + }; + } + } + } + }); + + // 创建权限 + if (!d[k].permission) { + d[k].permission = {}; + + for (const i in d[k]) { + d[k].permission[i] = `${d[k].namespace.replace( + "admin/", + "" + )}/${i}`.replace(/\//g, ":"); + } + } + + list.push(e); + } + } + } + + deep(service, 0); + }); + } + + if (isDev && c) { + await createDts(list); + } + } + + // 获取 + if (isDev) { + // 缓存数据 + set(storage.get("eps")); + + // 接口数据 + if (test.eps) { + getEps(); + } + } else { + const eps: any[] = []; + + JSON.parse(__EPS__).forEach((e: any) => { + const [prefix, api] = e; + + eps.push({ + prefix, + api: api.map((e: string[]) => { + const [method, path, name] = e; + + return { + method, + path, + name + }; + }) + }); + }); + + // 文件数据 + set({ eps }); + } +} diff --git a/src/cool/service/index.ts b/src/cool/service/index.ts new file mode 100644 index 0000000..ec25f75 --- /dev/null +++ b/src/cool/service/index.ts @@ -0,0 +1,23 @@ +import { deepFiles, deepMerge } from "../utils"; +import { BaseService } from "./base"; +import { useEps } from "./eps"; + +// 服务 +export const service: Service = { + request: new BaseService().request +}; + +export function useService() { + useEps(service); + + if (window.__modules__) { + window.__modules__.forEach((e: any) => { + deepMerge(service, deepFiles(e.service || [])); + }); + } + + return service; +} + +export * from "./base"; +export * from "./request"; diff --git a/src/service/request.ts b/src/cool/service/request.ts similarity index 75% rename from src/service/request.ts rename to src/cool/service/request.ts index d4be32f..542a1d8 100644 --- a/src/service/request.ts +++ b/src/cool/service/request.ts @@ -1,10 +1,10 @@ import axios from "axios"; -import store from "/@/store"; -import { isDev } from "/@/config/env"; -import { href, storage } from "/@/cool/utils"; import NProgress from "nprogress"; import "nprogress/nprogress.css"; import { ElMessage } from "element-plus"; +import { isDev, ignore } from "/@/cool/config"; +import { href, storage } from "/@/cool/utils"; +import { useBaseStore } from "/$/base"; axios.defaults.timeout = 30000; axios.defaults.withCredentials = true; @@ -19,22 +19,16 @@ let requests: Array = []; // Token 是否刷新中 let isRefreshing = false; -// 忽略规则 -const ignore = { - NProgress: ["/sys/info/record"], - token: ["/login", "/captcha"] -}; +// @ts-ignore +axios.interceptors.request.eject(axios._req); -// Request -axios.interceptors.request.use( +// @ts-ignore +axios._req = axios.interceptors.request.use( (config: any) => { - const token = store.getters.token || ""; + const { user } = useBaseStore(); if (config.url) { - if (!ignore.token.some((e) => config.url.includes(e))) { - config.headers["Authorization"] = token; - } - + // 请求进度条 if (!ignore.NProgress.some((e) => config.url.includes(e))) { NProgress.start(); } @@ -49,7 +43,10 @@ axios.interceptors.request.use( } // 验证 token - if (token) { + if (user.token) { + // 请求标识 + config.headers["Authorization"] = user.token; + if (config.url.includes("refreshToken")) { return config; } @@ -58,7 +55,7 @@ axios.interceptors.request.use( if (storage.isExpired("token")) { // 判断 refreshToken 是否过期 if (storage.isExpired("refreshToken")) { - store.dispatch("userRemove"); + user.clear(); return href("/login"); } @@ -66,11 +63,15 @@ axios.interceptors.request.use( if (!isRefreshing) { isRefreshing = true; - store.dispatch("refreshToken").then((token: string) => { - requests.forEach((cb) => cb(token)); - requests = []; - isRefreshing = false; - }); + user.refreshToken() + .then((token: string) => { + requests.forEach((cb) => cb(token)); + requests = []; + isRefreshing = false; + }) + .catch(() => { + user.clear(); + }); } return new Promise((resolve) => { @@ -91,21 +92,22 @@ axios.interceptors.request.use( } ); -// Response +// 响应 axios.interceptors.response.use( (res) => { NProgress.done(); - const { code, data, message } = res.data; - if (!res.data) { + if (!res?.data) { return res; } + const { code, data, message } = res.data; + switch (code) { case 1000: return data; default: - return Promise.reject(message); + return Promise.reject({ code, message }); } }, async (error) => { @@ -116,8 +118,7 @@ axios.interceptors.response.use( switch (status) { case 401: - await store.dispatch("userRemove"); - href("/login"); + href("/401"); break; case 403: @@ -147,10 +148,11 @@ axios.interceptors.response.use( default: console.error(status, config.url); + break; } } - return Promise.reject(error.message); + return Promise.reject({ message: error.message }); } ); diff --git a/src/cool/utils.ts b/src/cool/utils.ts deleted file mode 100644 index 4e1ab94..0000000 --- a/src/cool/utils.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./core/utils"; diff --git a/src/cool/core/utils/index.ts b/src/cool/utils/index.ts similarity index 56% rename from src/cool/core/utils/index.ts rename to src/cool/utils/index.ts index 910d68e..e33b118 100644 --- a/src/cool/core/utils/index.ts +++ b/src/cool/utils/index.ts @@ -1,7 +1,9 @@ -import { routerMode } from "/@/config/env"; +import { routerMode } from "../config"; import storage from "./storage"; +import module from "./module"; -export function isArray(value: any) { +// 是否Array类型 +export function isArray(value: any): boolean { if (typeof Array.isArray === "function") { return Array.isArray(value); } else { @@ -9,23 +11,42 @@ export function isArray(value: any) { } } -export function isObject(value: any) { +// 是否Object类型 +export function isObject(value: any): boolean { return Object.prototype.toString.call(value) === "[object Object]"; } -export function isNumber(value: any) { - return !isNaN(Number(value)); +// 是否Function类型 +export function isFunction(value: any): boolean { + return typeof value === "function"; } -export function isFunction(value: any) { - return typeof value == "function"; +// 是否String类型 +export function isString(value: any): boolean { + return typeof value === "string"; } -export function isString(value: any) { - return typeof value == "string"; +// 是否Boolean类型 +export function isBoolean(value: any): boolean { + return typeof value === "boolean"; } -export function isEmpty(value: any) { +// 是否数字类型 +export function isNumber(value: any): boolean { + return typeof value === "number" && !isNaN(value); +} + +// 是否Promise类型 +export function isPromise(value: any): boolean { + return ( + value !== null && + (typeof value === "object" || typeof value === "function") && + typeof value.then === "function" + ); +} + +// 是否为空 +export function isEmpty(value: any): boolean { if (isArray(value)) { return value.length === 0; } @@ -37,38 +58,54 @@ export function isEmpty(value: any) { return value === "" || value === undefined || value === null; } -export function isBoolean(value: any) { - return typeof value === "boolean"; +// 比较值 +export function compareValue(a: any, b: any) { + return String(a) === String(b); } -export function last(data: any) { +// 取最后一个值 +export function last(data: string | any[]) { if (isArray(data) || isString(data)) { return data[data.length - 1]; } } -export function cloneDeep(obj: any) { - const d = isArray(obj) ? obj : {}; +// 首字母大写 +export function firstUpperCase(value: string): string { + return value.replace(/\b(\w)(\w*)/g, function ($0, $1, $2) { + return $1.toUpperCase() + $2; + }); +} - if (isObject(obj)) { - for (const key in obj) { - if (obj[key]) { - if (obj[key] && typeof obj[key] === "object") { - d[key] = cloneDeep(obj[key]); +// 获取方法名 +export function getNames(value: any) { + return Object.getOwnPropertyNames(value.constructor.prototype); +} + +// 深拷贝 +export function cloneDeep(v: any) { + if (isObject(v)) { + const d: any = {}; + + for (const k in v) { + if (v.hasOwnProperty && v.hasOwnProperty(k)) { + if (v[k] && typeof v[k] === "object") { + d[k] = cloneDeep(v[k]); } else { - d[key] = obj[key]; + d[k] = v[k]; } } } + + return d; + } else if (isArray(v)) { + return v.map(cloneDeep); + } else { + return v; } - - return d; -} - -export function clone(obj: any) { - return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj)); } +// 深度合并 export function deepMerge(a: any, b: any) { let k; for (k in b) { @@ -78,31 +115,94 @@ export function deepMerge(a: any, b: any) { return a; } -export function contains(parent: any, node: any) { - while (node && (node = node.parentNode)) if (node === parent) return true; - return false; -} - -export function getUrlParam(name: string) { +// 获取地址栏参数 +export function getUrlParam(name: string): string | null { const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)"); const r = window.location.search.substr(1).match(reg); if (r != null) return decodeURIComponent(r[2]); return null; } -export function isPc() { - const userAgentInfo = navigator.userAgent; - const Agents = ["Android", "iPhone", "SymbianOS", "Windows Phone", "iPad", "iPod"]; - let flag = true; - for (let v = 0; v < Agents.length; v++) { - if (userAgentInfo.indexOf(Agents[v]) > 0) { - flag = false; - break; - } - } - return flag; +// 根据某个字段排序 +export function orderBy(list: any[], key: string): any[] { + return list.sort((a, b) => b[key] - a[key]); } +// 文件路径转对象 +export function deepFiles(list: any[]) { + const modules: any = {}; + + list.forEach((e) => { + const arr = e.path.split("/"); + const parents = arr.slice(0, arr.length - 1); + const name = basename(e.path).replace(".ts", ""); + + let curr: any = modules; + let prev: any = null; + let key: any = null; + + parents.forEach((k: string) => { + if (!curr[k]) { + curr[k] = {}; + } + + prev = curr; + curr = curr[k]; + key = k; + }); + + if (name == "index") { + prev[key] = e.value; + } else { + curr[name] = e.value; + } + }); + + return modules; +} + +// 文件名 +export function filename(path: string): string { + return basename(path.substring(0, path.lastIndexOf("."))); +} + +// 路径名称 +export function basename(path: string): string { + let index = path.lastIndexOf("/"); + index = index > -1 ? index : path.lastIndexOf("\\"); + if (index < 0) { + return path; + } + return path.substring(index + 1); +} + +// 文件扩展名 +export function extname(path: string): string { + return path.substring(path.lastIndexOf(".") + 1); +} + +// 横杠转驼峰 +export function toCamel(str: string): string { + return str.replace(/([^-])(?:-+([^-]))/g, function ($0, $1, $2) { + return $1 + $2.toUpperCase(); + }); +} + +// uuid +export function uuid(): string { + const s: any[] = []; + const hexDigits = "0123456789abcdef"; + for (let i = 0; i < 36; i++) { + s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); + } + s[14] = "4"; + s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); + s[8] = s[13] = s[18] = s[23] = "-"; + + return s.join(""); +} + +// 浏览器信息 export function getBrowser() { const { clientHeight, clientWidth } = document.documentElement; @@ -207,6 +307,7 @@ export function getBrowser() { }; } +// 跳转 export function href(path: string, newWindow?: boolean) { const { origin, pathname } = window.location; @@ -229,11 +330,8 @@ export function href(path: string, newWindow?: boolean) { } } -export function orderBy(list: Array, key: any) { - return list.sort((a, b) => a[key] - b[key]); -} - -export function deepTree(list: Array) { +// 列表转树形 +export function deepTree(list: any[]): any[] { const newList: Array = []; const map: any = {}; @@ -264,6 +362,7 @@ export function deepTree(list: Array) { return orderBy(newList, "orderNum"); } +// 树形转列表 export function revDeepTree(list: Array = []) { const d: Array = []; let id = 0; @@ -289,13 +388,4 @@ export function revDeepTree(list: Array = []) { return d; } -export function basename(path: string) { - let index = path.lastIndexOf("/"); - index = index > -1 ? index : path.lastIndexOf("\\"); - if (index < 0) { - return path; - } - return path.substring(index + 1); -} - -export { storage }; +export { storage, module }; diff --git a/src/cool/utils/module.ts b/src/cool/utils/module.ts new file mode 100644 index 0000000..5e836b4 --- /dev/null +++ b/src/cool/utils/module.ts @@ -0,0 +1,13 @@ +// @ts-nocheck + +const module = { + set(list: any[]) { + window.__modules__ = list; + }, + + get(name: string) { + return name ? window.__modules__.find((e) => e.name == name) : window.__modules__; + } +}; + +export default module; diff --git a/src/cool/core/utils/storage.ts b/src/cool/utils/storage.ts similarity index 100% rename from src/cool/core/utils/storage.ts rename to src/cool/utils/storage.ts diff --git a/src/env.d.ts b/src/env.d.ts new file mode 100644 index 0000000..06e2688 --- /dev/null +++ b/src/env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/src/index.vue b/src/index.vue new file mode 100644 index 0000000..46b0496 --- /dev/null +++ b/src/index.vue @@ -0,0 +1,343 @@ + + + + + + + diff --git a/src/main.ts b/src/main.ts index 7c1817f..f6dc18f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,46 +1,17 @@ import { createApp } from "vue"; import App from "./App.vue"; - -// cool import { bootstrap } from "./cool"; -// router -import router from "./router"; - -// store -import store from "./store"; - // mock -import "./mock"; - -// element-plus -import ElementPlus from "element-plus"; -import "element-plus/theme-chalk/src/index.scss"; - -// mitt -import mitt from "mitt"; - -// echarts -import VueECharts from "vue-echarts"; +// import "./mock"; const app = createApp(App); +// 启动 bootstrap(app) .then(() => { - // echarts 可视图表 - app.component("v-chart", VueECharts); - - // 事件通讯 - app.provide("mitt", mitt()); - - app.use(store).use(router).use(ElementPlus).mount("#app"); + app.mount("#app"); }) - .catch((err: string) => { - console.error(`COOL-ADMIN 启动失败`, err); + .catch((err) => { + console.error("COOL-ADMIN 启动失败", err); }); - -// 应用加载 -store.dispatch("appLoad"); - -// @ts-ignore -window.__app__ = app; diff --git a/src/mock/index.ts b/src/mock/index.ts index 389d275..e9ad5d8 100644 --- a/src/mock/index.ts +++ b/src/mock/index.ts @@ -1,6 +1,3 @@ // @ts-nocheck - -import "./chat"; - const xhr = new window._XMLHttpRequest(); window.XMLHttpRequest.prototype.upload = xhr.upload; diff --git a/src/modules/base/common/index.ts b/src/modules/base/common/index.ts new file mode 100644 index 0000000..e4a6999 --- /dev/null +++ b/src/modules/base/common/index.ts @@ -0,0 +1,4 @@ +import "./resize"; + +export * from "./theme"; +export * from "./permission"; diff --git a/src/modules/base/common/permission.ts b/src/modules/base/common/permission.ts new file mode 100644 index 0000000..0912ad0 --- /dev/null +++ b/src/modules/base/common/permission.ts @@ -0,0 +1,30 @@ +import { useBaseStore } from "../store"; +import { isObject } from "/@/cool/utils"; + +function parse(value: any) { + const { menu } = useBaseStore(); + + if (typeof value == "string") { + return value ? menu.perms.some((e: any) => e.includes(value.replace(/\s/g, ""))) : false; + } else { + return Boolean(value); + } +} + +export function checkPerm(value: any) { + if (!value) { + return false; + } + + if (isObject(value)) { + if (value.or) { + return value.or.some(parse); + } + + if (value.and) { + return value.and.some((e: any) => !parse(e)) ? false : true; + } + } + + return parse(value); +} diff --git a/src/modules/base/common/resize.ts b/src/modules/base/common/resize.ts new file mode 100644 index 0000000..2095b2b --- /dev/null +++ b/src/modules/base/common/resize.ts @@ -0,0 +1,12 @@ +import { useBaseStore } from "../store"; + +function resize() { + const { app } = useBaseStore(); + app.setBrowser(); + app.isFold = app.browser.isMini; +} + +window.onload = function () { + window.addEventListener("resize", resize); + resize(); +}; diff --git a/src/cool/modules/base/common/theme.ts b/src/modules/base/common/theme.ts similarity index 75% rename from src/cool/modules/base/common/theme.ts rename to src/modules/base/common/theme.ts index 88fcf51..ceb5673 100644 --- a/src/cool/modules/base/common/theme.ts +++ b/src/modules/base/common/theme.ts @@ -1,9 +1,8 @@ -import { iconfontUrl, app } from "/@/config/env"; +import { iconfont, app } from "/@/cool/config"; import { basename } from "/@/cool/utils"; import { createLink } from "../utils"; // 主题初始化 - if (app.theme) { const { url, color } = app.theme; @@ -15,13 +14,16 @@ if (app.theme) { } // 字体图标库加载 - -if (iconfontUrl) { - createLink(iconfontUrl); +if (iconfont) { + iconfont.forEach((e: string) => { + createLink(e); + }); } -// svg 图标加载 +// 默认 +createLink("//at.alicdn.com/t/font_3254019_60a2xxj8uus.css"); +// svg 图标加载 const svgFiles = import.meta.globEager("/src/icons/svg/**/*.svg"); function iconList() { diff --git a/src/cool/modules/base/components/avatar/index.vue b/src/modules/base/components/avatar/index.vue similarity index 88% rename from src/cool/modules/base/components/avatar/index.vue rename to src/modules/base/components/avatar/index.vue index 679f71f..d71c203 100644 --- a/src/cool/modules/base/components/avatar/index.vue +++ b/src/modules/base/components/avatar/index.vue @@ -1,9 +1,9 @@ - diff --git a/src/cool/modules/base/components/route-nav/index.vue b/src/modules/base/pages/layout/components/route-nav.vue similarity index 77% rename from src/cool/modules/base/components/route-nav/index.vue rename to src/modules/base/pages/layout/components/route-nav.vue index 61e1a8e..0e61154 100644 --- a/src/cool/modules/base/components/route-nav/index.vue +++ b/src/modules/base/pages/layout/components/route-nav.vue @@ -1,6 +1,6 @@ - +