发布5.7.0
|
@ -1,5 +1,5 @@
|
|||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
# 🎨 editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
# 🎨 editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_style = tab
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
|
|
@ -56,6 +56,7 @@ module.exports = {
|
|||
"vue/html-closing-bracket-newline": "off",
|
||||
"vue/max-attributes-per-line": "off",
|
||||
"vue/multiline-html-element-content-newline": "off",
|
||||
"vue/multi-word-component-names": "off",
|
||||
"vue/singleline-html-element-content-newline": "off",
|
||||
"vue/attribute-hyphenation": "off",
|
||||
"vue/html-self-closing": "off",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.js text eol=lf
|
||||
*.json text eol=lf
|
||||
*.ts text eol=lf
|
||||
*.vue text eol=lf
|
|
@ -1,5 +1,5 @@
|
|||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
node_modules
|
||||
.DS_Store
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
|
16
.prettierrc
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
{
|
||||
"tabWidth": 4,
|
||||
"useTabs": true,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"printWidth": 100,
|
||||
"trailingComma": "none"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"module-config": {
|
||||
"prefix": "module-config",
|
||||
"scope": "typescript",
|
||||
"body": [
|
||||
"import { ModuleConfig } from \"/@/cool\";",
|
||||
"",
|
||||
"export default (): ModuleConfig => {",
|
||||
" return {};",
|
||||
"};",
|
||||
""
|
||||
],
|
||||
"description": "module config snippets"
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
{
|
||||
"cl-crud": {
|
||||
"prefix": "cl-crud",
|
||||
"scope": "vue",
|
||||
"body": [
|
||||
"<template>",
|
||||
" <cl-crud ref=\"Crud\">",
|
||||
|
@ -32,13 +33,11 @@
|
|||
" </cl-crud>",
|
||||
"</template>",
|
||||
"",
|
||||
"<script lang=\"ts\" setup>",
|
||||
"<script lang=\"ts\" name=\"菜单名称\" setup>",
|
||||
"import { useCrud, useTable, useUpsert } from \"@cool-vue/crud\";",
|
||||
"import { useCool } from \"/@/cool\";",
|
||||
"",
|
||||
"const { service, named } = useCool();",
|
||||
"",
|
||||
"named(\"菜单名称\");",
|
||||
"const { service } = useCool();",
|
||||
"",
|
||||
"// cl-upsert 配置",
|
||||
"const Upsert = useUpsert({",
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{
|
||||
"editor.cursorSmoothCaretAnimation": true
|
||||
"editor.cursorSmoothCaretAnimation": true,
|
||||
"editor.formatOnSave": true,
|
||||
}
|
||||
|
|
30
Dockerfile
|
@ -1,16 +1,16 @@
|
|||
FROM node:lts-alpine
|
||||
WORKDIR /build
|
||||
# 设置Node-Sass的镜像地址
|
||||
RUN npm config set sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
|
||||
# 设置npm镜像
|
||||
RUN npm config set registry https://registry.npm.taobao.org
|
||||
COPY package.json /build/package.json
|
||||
RUN yarn
|
||||
COPY ./ /build
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx
|
||||
RUN mkdir /app
|
||||
COPY --from=0 /build/dist /app
|
||||
COPY --from=0 /build/nginx.conf /etc/nginx/nginx.conf
|
||||
FROM node:lts-alpine
|
||||
WORKDIR /build
|
||||
# 设置Node-Sass的镜像地址
|
||||
RUN npm config set sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
|
||||
# 设置npm镜像
|
||||
RUN npm config set registry https://registry.npm.taobao.org
|
||||
COPY package.json /build/package.json
|
||||
RUN yarn
|
||||
COPY ./ /build
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx
|
||||
RUN mkdir /app
|
||||
COPY --from=0 /build/dist /app
|
||||
COPY --from=0 /build/nginx.conf /etc/nginx/nginx.conf
|
||||
EXPOSE 80
|
42
LICENSE
|
@ -1,21 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2021 cool-team-official
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 cool-team-official
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
import { Plugin } from "vite";
|
||||
import { parseJson } from "./utils";
|
||||
import { getModules } from "./lib/modules";
|
||||
import { createEps, getEps } from "./lib/eps";
|
||||
import { createMenu } from "./lib/menu";
|
||||
import { createEps, createMenu, createSvg, createTag, getEps, getModules } from "./lib";
|
||||
|
||||
export const cool = (): Plugin | null => {
|
||||
export function cool(): Plugin {
|
||||
return {
|
||||
name: "vite-cool",
|
||||
enforce: "pre",
|
||||
configureServer(server) {
|
||||
server.middlewares.use(async (req, res, next) => {
|
||||
function done(data: any) {
|
||||
|
@ -14,10 +13,9 @@ export const cool = (): Plugin | null => {
|
|||
res.end(JSON.stringify(data));
|
||||
}
|
||||
|
||||
// 自定义
|
||||
if (req.url.includes("__cool")) {
|
||||
if (req.url?.includes("__cool")) {
|
||||
const body = await parseJson(req);
|
||||
let next: any = null;
|
||||
let next: any;
|
||||
|
||||
switch (req.url) {
|
||||
// 快速创建菜单
|
||||
|
@ -54,6 +52,12 @@ export const cool = (): Plugin | null => {
|
|||
}
|
||||
});
|
||||
},
|
||||
transform(code, id) {
|
||||
return createTag(code, id);
|
||||
},
|
||||
transformIndexHtml(html) {
|
||||
return createSvg(html);
|
||||
},
|
||||
config() {
|
||||
return {
|
||||
define: {
|
||||
|
@ -62,4 +66,4 @@ export const cool = (): Plugin | null => {
|
|||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,22 +1,17 @@
|
|||
export default {
|
||||
// 实体生成
|
||||
entity: {
|
||||
// 是否生成
|
||||
enable: true,
|
||||
mapping: [
|
||||
{
|
||||
// 自定义匹配
|
||||
custom: ({ entityName, propertyName, type }) => {
|
||||
// status原本是tinyint,如果是1的话,== true是可以的,但是不能 === true,请谨慎使用
|
||||
// status 原本是tinyint,如果是1的话,== true 是可以的,但是不能 === true,请谨慎使用
|
||||
if (propertyName === "status" && type == "tinyint") return "boolean";
|
||||
// 如果没有,返回null或者不返回,则继续遍历其他匹配规则
|
||||
return null;
|
||||
}
|
||||
},
|
||||
{
|
||||
// 返回类型
|
||||
type: "string",
|
||||
// 匹配列类型
|
||||
includes: ["varchar", "text"]
|
||||
},
|
||||
{
|
||||
|
|
|
@ -3,73 +3,73 @@ import { isEmpty, last } from "lodash";
|
|||
import { createDir, firstUpperCase, readFile, toCamel } from "../../utils";
|
||||
import { createWriteStream } from "fs";
|
||||
import { join } from "path";
|
||||
// import * as config from "/@/cool/config";
|
||||
import config from "./config";
|
||||
|
||||
// 临时目录路径
|
||||
const tempPath = join(__dirname, "../../temp");
|
||||
|
||||
// 创建描述文件
|
||||
export async function createEps({ list, service }: any) {
|
||||
const t0 = [
|
||||
[
|
||||
`
|
||||
declare interface Crud {
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data: any): Promise<any>;
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data: { ids?: number[] | string[]; [key: string]: any }): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data: { id?: number | string; [key: string]: any }): Promise<any>;
|
||||
/**
|
||||
* 详情
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data: { id?: number | string; [key: string]: any }): Promise<any>;
|
||||
/**
|
||||
* 全部
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: { page?: number | string; size?: number | string; [key: string]: any }): Promise<PageResponse>;
|
||||
}
|
||||
`,
|
||||
// 获取类型
|
||||
function getType({ entityName, propertyName, type }) {
|
||||
for (const map of config.entity.mapping) {
|
||||
if (map.custom) {
|
||||
const resType = map.custom({ entityName, propertyName, type });
|
||||
if (resType) return resType;
|
||||
}
|
||||
if (map.includes?.includes(type)) return map.type;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
`
|
||||
declare interface PageResponse {
|
||||
list: any[];
|
||||
pagination: { size: number; page: number; total: number };
|
||||
[key: string]: any;
|
||||
}
|
||||
`,
|
||||
// 创建 Entity
|
||||
function createEntity({ list }: any) {
|
||||
const t0: any[] = [];
|
||||
|
||||
`
|
||||
declare interface RequestOptions {
|
||||
params?: any;
|
||||
data?: any;
|
||||
url: string;
|
||||
method?: "GET" | "get" | "POST" | "post" | string;
|
||||
[key: string]: any;
|
||||
}
|
||||
`
|
||||
]
|
||||
for (const item of list) {
|
||||
if (!item.name) continue;
|
||||
const t = [`interface ${item.name} {`];
|
||||
for (const col of item.columns) {
|
||||
// 描述
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(` * ${col.comment}\n`);
|
||||
t.push(" */\n");
|
||||
t.push(
|
||||
`${col.propertyName}?: ${getType({
|
||||
entityName: item.name,
|
||||
propertyName: col.propertyName,
|
||||
type: col.type
|
||||
})};`
|
||||
);
|
||||
}
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(` * 任意键值\n`);
|
||||
t.push(" */\n");
|
||||
t.push(`[key: string]: any;`);
|
||||
t.push("}");
|
||||
t0.push(t);
|
||||
}
|
||||
|
||||
return t0.map((e) => e.join("")).join("\n\n");
|
||||
}
|
||||
|
||||
// 创建 Service
|
||||
function createService({ list, service }: any) {
|
||||
const t0: any[] = [];
|
||||
|
||||
const t1 = [
|
||||
`type Service = {
|
||||
request(options: {
|
||||
url: string;
|
||||
method?: 'POST' | 'GET' | string;
|
||||
data?: any;
|
||||
params?: any;
|
||||
proxy?: boolean;
|
||||
[key: string]: any;
|
||||
}): Promise<any>;
|
||||
`
|
||||
];
|
||||
|
||||
const t1 = [`declare type Service = {`, `request(data: RequestOptions): Promise<any>;`];
|
||||
|
||||
// 处理数据
|
||||
function deep(d: any, k?: string) {
|
||||
if (!k) k = "";
|
||||
|
@ -82,9 +82,7 @@ export async function createEps({ list, service }: any) {
|
|||
const item = list.find((e: any) => (e.prefix || "").includes(d[i].namespace));
|
||||
|
||||
if (item) {
|
||||
const t = [
|
||||
`declare interface ${name} ${item.extendCrud ? " extends Crud" : ""} {`
|
||||
];
|
||||
const t = [`interface ${name} {`];
|
||||
|
||||
t1.push(`${i}: ${name};`);
|
||||
|
||||
|
@ -132,10 +130,28 @@ export async function createEps({ list, service }: any) {
|
|||
// 返回类型
|
||||
let res = "";
|
||||
|
||||
// 实体名
|
||||
const en = item.name || "any";
|
||||
|
||||
switch (a.path) {
|
||||
case "/page":
|
||||
res = "PageResponse";
|
||||
res = `
|
||||
{
|
||||
pagination: { size: number; page: number; total: number };
|
||||
list: ${en} [];
|
||||
[key: string]: any;
|
||||
}
|
||||
`;
|
||||
break;
|
||||
|
||||
case "/list":
|
||||
res = `${en} []`;
|
||||
break;
|
||||
|
||||
case "/info":
|
||||
res = en;
|
||||
break;
|
||||
|
||||
default:
|
||||
res = "any";
|
||||
break;
|
||||
|
@ -145,7 +161,6 @@ export async function createEps({ list, service }: any) {
|
|||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(` * ${a.summary || n}\n`);
|
||||
t.push(` * @returns Promise<${res}>\n`);
|
||||
t.push(" */\n");
|
||||
|
||||
t.push(
|
||||
|
@ -155,15 +170,28 @@ export async function createEps({ list, service }: any) {
|
|||
);
|
||||
}
|
||||
|
||||
permission.push(`${n}: string;`);
|
||||
permission.push(n);
|
||||
});
|
||||
|
||||
// 添加权限
|
||||
// 权限标识
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(" * 权限\n");
|
||||
t.push(" * 权限标识\n");
|
||||
t.push(" */\n");
|
||||
t.push(`permission: { ${permission.join("\n")} }`);
|
||||
t.push(
|
||||
`permission: { ${permission.map((e) => `${e}: string;`).join("\n")} };`
|
||||
);
|
||||
|
||||
// 权限状态
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(" * 权限状态\n");
|
||||
t.push(" */\n");
|
||||
t.push(
|
||||
`_permission: { ${permission
|
||||
.map((e) => `${e}: boolean;`)
|
||||
.join("\n")} };`
|
||||
);
|
||||
}
|
||||
|
||||
t.push("}");
|
||||
|
@ -177,14 +205,30 @@ export async function createEps({ list, service }: any) {
|
|||
}
|
||||
}
|
||||
|
||||
// 深度
|
||||
deep(service);
|
||||
|
||||
// 结束
|
||||
t1.push("}");
|
||||
|
||||
// 追加
|
||||
t0.push(t1);
|
||||
|
||||
return t0.map((e) => e.join("")).join("\n\n");
|
||||
}
|
||||
|
||||
// 创建描述文件
|
||||
export async function createEps({ list, service }: any) {
|
||||
// 文件内容
|
||||
const text = `
|
||||
declare namespace Eps {
|
||||
${createEntity({ list })}
|
||||
${createService({ list, service })}
|
||||
}
|
||||
`;
|
||||
|
||||
// 文本内容
|
||||
const content = prettier.format(t0.map((e) => e.join("")).join("\n\n"), {
|
||||
const content = prettier.format(text, {
|
||||
parser: "typescript",
|
||||
useTabs: true,
|
||||
tabWidth: 4,
|
||||
|
@ -198,12 +242,12 @@ export async function createEps({ list, service }: any) {
|
|||
// 创建 temp 目录
|
||||
createDir(tempPath);
|
||||
|
||||
// 创建 service 描述文件
|
||||
createWriteStream(join(tempPath, "service.d.ts"), {
|
||||
// 创建 eps 描述文件
|
||||
createWriteStream(join(tempPath, "eps.d.ts"), {
|
||||
flags: "w"
|
||||
}).write(content);
|
||||
|
||||
// 创建 eps 文件
|
||||
// 创建 eps 数据文件
|
||||
createWriteStream(join(tempPath, "eps.json"), {
|
||||
flags: "w"
|
||||
}).write(
|
||||
|
@ -213,71 +257,9 @@ export async function createEps({ list, service }: any) {
|
|||
})
|
||||
)
|
||||
);
|
||||
|
||||
if (config.entity.enable) createEntity(list);
|
||||
}
|
||||
|
||||
// 获取描述
|
||||
export function getEps() {
|
||||
return JSON.stringify(readFile(join(tempPath, "eps.json")));
|
||||
}
|
||||
|
||||
// 获取类型
|
||||
function getType({ entityName, propertyName, type }) {
|
||||
for (const map of config.entity.mapping) {
|
||||
if (map.custom) {
|
||||
const resType = map.custom({ entityName, propertyName, type });
|
||||
if (resType) return resType;
|
||||
}
|
||||
if (map.includes?.includes(type)) return map.type;
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
// 创建Entity描述文件
|
||||
export function createEntity(list: any[]) {
|
||||
const t2: any[] = [];
|
||||
|
||||
for (const item of list) {
|
||||
if (!item.name) continue;
|
||||
const t = [`declare interface ${item.name} {`];
|
||||
for (const col of item.columns) {
|
||||
// 描述
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(` * ${col.comment}\n`);
|
||||
t.push(" */\n");
|
||||
t.push(
|
||||
`${col.propertyName}?: ${getType({
|
||||
entityName: item.name,
|
||||
propertyName: col.propertyName,
|
||||
type: col.type
|
||||
})};`
|
||||
);
|
||||
}
|
||||
t.push("\n");
|
||||
t.push("/**\n");
|
||||
t.push(` * 任意键值\n`);
|
||||
t.push(" */\n");
|
||||
t.push(`[key: string]: any;`);
|
||||
t.push("}");
|
||||
t2.push(t);
|
||||
}
|
||||
|
||||
// 文本内容
|
||||
const content = prettier.format(t2.map((e) => e.join("")).join("\n\n"), {
|
||||
parser: "typescript",
|
||||
useTabs: true,
|
||||
tabWidth: 4,
|
||||
endOfLine: "lf",
|
||||
semi: true,
|
||||
singleQuote: false,
|
||||
printWidth: 100,
|
||||
trailingComma: "none"
|
||||
});
|
||||
|
||||
// 创建 entity 描述文件
|
||||
createWriteStream(join(tempPath, "entity.d.ts"), {
|
||||
flags: "w"
|
||||
}).write(content);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
export * from "./eps";
|
||||
export * from "./menu";
|
||||
export * from "./module";
|
||||
export * from "./svg";
|
||||
export * from "./tag";
|
|
@ -1,7 +1,7 @@
|
|||
import { createWriteStream } from "fs";
|
||||
import prettier from "prettier";
|
||||
import { join } from "path";
|
||||
import { createDir } from "../../utils";
|
||||
import { mkdirs } from "../../utils";
|
||||
import rules from "./rules";
|
||||
import { isFunction, isRegExp, isString } from "lodash";
|
||||
|
||||
|
@ -108,7 +108,7 @@ const handler = {
|
|||
function createComponent(item: any) {
|
||||
const { propertyName: prop, comment: label } = item;
|
||||
|
||||
let d = null;
|
||||
let d: any = null;
|
||||
|
||||
rules.forEach((r: any) => {
|
||||
const s = r.test.find((e: any) => {
|
||||
|
@ -172,7 +172,7 @@ function getPageName(router: string) {
|
|||
router = router.substr(1, router.length);
|
||||
}
|
||||
|
||||
return router ? router.replace("/", "-") : "";
|
||||
return router ? router.replace(/\//g, "-") : "";
|
||||
}
|
||||
|
||||
// 时间合并
|
||||
|
@ -198,7 +198,7 @@ function datetimeMerge({ columns, item }: any) {
|
|||
}
|
||||
|
||||
// 创建文件
|
||||
export async function createMenu({ router, columns, prefix, api, module, filename }: any): void {
|
||||
export async function createMenu({ router, columns, prefix, api, filePath }: any) {
|
||||
const upsert: any = {
|
||||
items: []
|
||||
};
|
||||
|
@ -310,13 +310,11 @@ export async function createMenu({ router, columns, prefix, api, module, filenam
|
|||
</cl-crud>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts" name="${getPageName(router)}" setup>
|
||||
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { service, named } = useCool();
|
||||
|
||||
named("${getPageName(router)}");
|
||||
const { service } = useCool();
|
||||
|
||||
// cl-upsert 配置
|
||||
const Upsert = useUpsert(${JSON.stringify(upsert)});
|
||||
|
@ -335,6 +333,7 @@ const Crud = useCrud(
|
|||
);
|
||||
</script>`;
|
||||
|
||||
// 文件内容
|
||||
const content = prettier.format(temp, {
|
||||
parser: "vue",
|
||||
useTabs: true,
|
||||
|
@ -347,14 +346,17 @@ const Crud = useCrud(
|
|||
trailingComma: "none"
|
||||
});
|
||||
|
||||
// views 目录是否存在
|
||||
const dir = join(__dirname, `../../../../src/modules/${module}/views`);
|
||||
// 目录路径
|
||||
const dir = filePath.split("/");
|
||||
|
||||
// 文件名
|
||||
const fname = dir.pop();
|
||||
|
||||
// 创建目录
|
||||
createDir(dir);
|
||||
const path = mkdirs(`./src/modules/${dir.join("/")}`);
|
||||
|
||||
// 创建文件
|
||||
createWriteStream(join(dir, `${filename}.vue`), {
|
||||
createWriteStream(join(path, fname), {
|
||||
flags: "w"
|
||||
}).write(content);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
import fs from "fs";
|
||||
|
||||
export function getModules() {
|
||||
return new Promise((resolve, reject) => {
|
||||
try {
|
||||
const dirs = fs.readdirSync("./src/modules");
|
||||
resolve(dirs.filter((e) => !e.includes(".")));
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
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 Promise.reject(e);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
import { readFileSync, readdirSync } from "fs";
|
||||
import { extname } from "path";
|
||||
|
||||
function findFiles(dir: string): string[] {
|
||||
const res: string[] = [];
|
||||
const dirs = readdirSync(dir, {
|
||||
withFileTypes: true
|
||||
});
|
||||
for (const d of dirs) {
|
||||
if (d.isDirectory()) {
|
||||
res.push(...findFiles(dir + d.name + "/"));
|
||||
} else {
|
||||
if (extname(d.name) == ".svg") {
|
||||
const svg = readFileSync(dir + d.name)
|
||||
.toString()
|
||||
.replace(/(\r)|(\n)/g, "")
|
||||
.replace(/<svg([^>+].*?)>/, (_: any, $2: any) => {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
let content = $2.replace(
|
||||
/(width|height)="([^>+].*?)"/g,
|
||||
(_: any, s2: any, s3: any) => {
|
||||
if (s2 === "width") {
|
||||
width = s3;
|
||||
} else if (s2 === "height") {
|
||||
height = s3;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
);
|
||||
if (!/(viewBox="[^>+].*?")/g.test($2)) {
|
||||
content += `viewBox="0 0 ${width} ${height}"`;
|
||||
}
|
||||
return `<symbol id="icon-${d.name.replace(".svg", "")}" ${content}>`;
|
||||
})
|
||||
.replace("</svg>", "</symbol>");
|
||||
res.push(svg);
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export function createSvg(html: string) {
|
||||
const res = findFiles("./src/modules/");
|
||||
|
||||
return html.replace(
|
||||
"<body>",
|
||||
`<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
|
||||
${res.join("")}
|
||||
</svg>`
|
||||
);
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { parse, compileScript } from "@vue/compiler-sfc";
|
||||
import magicString from "magic-string";
|
||||
|
||||
export function createTag(code: string, id: string) {
|
||||
if (/\.vue$/.test(id)) {
|
||||
let s: any;
|
||||
const str = () => s || (s = new magicString(code));
|
||||
const { descriptor } = parse(code);
|
||||
|
||||
if (!descriptor.script && descriptor.scriptSetup) {
|
||||
const res = compileScript(descriptor, { id });
|
||||
const { name, lang }: any = res.attrs;
|
||||
|
||||
str().appendLeft(
|
||||
0,
|
||||
`<script lang="${lang}">
|
||||
import { defineComponent } from 'vue'
|
||||
export default defineComponent({
|
||||
name: "${name}"
|
||||
})
|
||||
<\/script>`
|
||||
);
|
||||
|
||||
return {
|
||||
map: str().generateMap(),
|
||||
code: str().toString()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
|
@ -1,507 +0,0 @@
|
|||
declare interface BaseSysDepartmentEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 部门名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 上级部门ID
|
||||
*/
|
||||
parentId?: BigInt;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
orderNum?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface BaseSysLogEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
userId?: BigInt;
|
||||
/**
|
||||
* 行为
|
||||
*/
|
||||
action?: string;
|
||||
/**
|
||||
* ip
|
||||
*/
|
||||
ip?: string;
|
||||
/**
|
||||
* ip地址
|
||||
*/
|
||||
ipAddr?: string;
|
||||
/**
|
||||
* 参数
|
||||
*/
|
||||
params?: string;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface BaseSysMenuEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 父菜单ID
|
||||
*/
|
||||
parentId?: BigInt;
|
||||
/**
|
||||
* 菜单名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 菜单地址
|
||||
*/
|
||||
router?: string;
|
||||
/**
|
||||
* 权限标识
|
||||
*/
|
||||
perms?: string;
|
||||
/**
|
||||
* 类型 0:目录 1:菜单 2:按钮
|
||||
*/
|
||||
type?: number;
|
||||
/**
|
||||
* 图标
|
||||
*/
|
||||
icon?: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
orderNum?: number;
|
||||
/**
|
||||
* 视图地址
|
||||
*/
|
||||
viewPath?: string;
|
||||
/**
|
||||
* 路由缓存
|
||||
*/
|
||||
keepAlive?: boolean;
|
||||
/**
|
||||
* 是否显示
|
||||
*/
|
||||
isShow?: boolean;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface BaseSysParamEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 键位
|
||||
*/
|
||||
keyName?: string;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
data?: string;
|
||||
/**
|
||||
* 数据类型 0:字符串 1:数组 2:键值对
|
||||
*/
|
||||
dataType?: number;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface BaseSysRoleEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 用户ID
|
||||
*/
|
||||
userId?: string;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 角色标签
|
||||
*/
|
||||
label?: string;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 数据权限是否关联上下级
|
||||
*/
|
||||
relevance?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface BaseSysUserEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 部门ID
|
||||
*/
|
||||
departmentId?: BigInt;
|
||||
/**
|
||||
* 姓名
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 用户名
|
||||
*/
|
||||
username?: string;
|
||||
/**
|
||||
* 密码
|
||||
*/
|
||||
password?: string;
|
||||
/**
|
||||
* 密码版本, 作用是改完密码,让原来的token失效
|
||||
*/
|
||||
passwordV?: number;
|
||||
/**
|
||||
* 昵称
|
||||
*/
|
||||
nickName?: string;
|
||||
/**
|
||||
* 头像
|
||||
*/
|
||||
headImg?: string;
|
||||
/**
|
||||
* 手机
|
||||
*/
|
||||
phone?: string;
|
||||
/**
|
||||
* 邮箱
|
||||
*/
|
||||
email?: string;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 状态 0:禁用 1:启用
|
||||
*/
|
||||
status?: boolean;
|
||||
/**
|
||||
* socketId
|
||||
*/
|
||||
socketId?: string;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface DemoGoodsEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 标题
|
||||
*/
|
||||
title?: string;
|
||||
/**
|
||||
* 图片
|
||||
*/
|
||||
pic?: string;
|
||||
/**
|
||||
* 价格
|
||||
*/
|
||||
price?: number;
|
||||
/**
|
||||
* 分类
|
||||
*/
|
||||
type?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface DictInfoEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 类型ID
|
||||
*/
|
||||
typeId?: number;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 排序
|
||||
*/
|
||||
orderNum?: number;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface DictTypeEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 标识
|
||||
*/
|
||||
key?: string;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface SpaceInfoEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 地址
|
||||
*/
|
||||
url?: string;
|
||||
/**
|
||||
* 类型
|
||||
*/
|
||||
type?: string;
|
||||
/**
|
||||
* 分类ID
|
||||
*/
|
||||
classifyId?: BigInt;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface SpaceTypeEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 类别名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* 父分类ID
|
||||
*/
|
||||
parentId?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
declare interface TaskInfoEntity {
|
||||
/**
|
||||
* ID
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
* 任务ID
|
||||
*/
|
||||
jobId?: string;
|
||||
/**
|
||||
* 任务配置
|
||||
*/
|
||||
repeatConf?: string;
|
||||
/**
|
||||
* 名称
|
||||
*/
|
||||
name?: string;
|
||||
/**
|
||||
* cron
|
||||
*/
|
||||
cron?: string;
|
||||
/**
|
||||
* 最大执行次数 不传为无限次
|
||||
*/
|
||||
limit?: number;
|
||||
/**
|
||||
* 每间隔多少毫秒执行一次 如果cron设置了 这项设置就无效
|
||||
*/
|
||||
every?: number;
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
remark?: string;
|
||||
/**
|
||||
* 状态 0:停止 1:运行
|
||||
*/
|
||||
status?: boolean;
|
||||
/**
|
||||
* 开始时间
|
||||
*/
|
||||
startDate?: Date;
|
||||
/**
|
||||
* 结束时间
|
||||
*/
|
||||
endDate?: Date;
|
||||
/**
|
||||
* 数据
|
||||
*/
|
||||
data?: string;
|
||||
/**
|
||||
* 执行的service实例ID
|
||||
*/
|
||||
service?: string;
|
||||
/**
|
||||
* 状态 0:系统 1:用户
|
||||
*/
|
||||
type?: number;
|
||||
/**
|
||||
* 下一次执行时间
|
||||
*/
|
||||
nextRunTime?: Date;
|
||||
/**
|
||||
* 状态 0:cron 1:时间间隔
|
||||
*/
|
||||
taskType?: number;
|
||||
/**
|
||||
* 创建时间
|
||||
*/
|
||||
createTime?: Date;
|
||||
/**
|
||||
* 更新时间
|
||||
*/
|
||||
updateTime?: Date;
|
||||
/**
|
||||
* 任意键值
|
||||
*/
|
||||
[key: string]: any;
|
||||
}
|
|
@ -1,952 +0,0 @@
|
|||
declare interface Crud {
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data: any): Promise<any>;
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data: { ids?: number[] | string[]; [key: string]: any }): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data: { id?: number | string; [key: string]: any }): Promise<any>;
|
||||
/**
|
||||
* 详情
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data: { id?: number | string; [key: string]: any }): Promise<any>;
|
||||
/**
|
||||
* 全部
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: {
|
||||
page?: number | string;
|
||||
size?: number | string;
|
||||
[key: string]: any;
|
||||
}): Promise<PageResponse>;
|
||||
}
|
||||
|
||||
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<any>
|
||||
*/
|
||||
personUpdate(data?: any): Promise<any>;
|
||||
/**
|
||||
* 文件上传模式
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
uploadMode(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限与菜单
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
permmenu(data?: any): Promise<any>;
|
||||
/**
|
||||
* 个人信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
person(data?: any): Promise<any>;
|
||||
/**
|
||||
* 文件上传
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
upload(data?: any): Promise<any>;
|
||||
/**
|
||||
* 退出
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
logout(data?: any): Promise<any>;
|
||||
/**
|
||||
* list
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* page
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* info
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* update
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* delete
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* add
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
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<any>
|
||||
*/
|
||||
refreshToken(data?: any): Promise<any>;
|
||||
/**
|
||||
* 验证码
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
captcha(data?: any): Promise<any>;
|
||||
/**
|
||||
* 登录
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
login(data?: any): Promise<any>;
|
||||
/**
|
||||
* 获得网页内容的参数值
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
html(data?: any): Promise<any>;
|
||||
/**
|
||||
* 实体信息与路径
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
eps(data?: any): Promise<any>;
|
||||
/**
|
||||
* list
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* page
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* info
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* update
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* delete
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* add
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
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<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 排序
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
order(data?: any): Promise<any>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* page
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* info
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
order: string;
|
||||
list: string;
|
||||
add: string;
|
||||
page: string;
|
||||
info: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface BaseSysLog {
|
||||
/**
|
||||
* 日志保存时间
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
setKeep(data?: any): Promise<any>;
|
||||
/**
|
||||
* 获得日志保存时间
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
getKeep(data?: any): Promise<any>;
|
||||
/**
|
||||
* 清理
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
clear(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* list
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* info
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* update
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* delete
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* add
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
setKeep: string;
|
||||
getKeep: string;
|
||||
clear: string;
|
||||
page: string;
|
||||
list: string;
|
||||
info: string;
|
||||
update: string;
|
||||
delete: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface BaseSysMenu {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
info: string;
|
||||
list: string;
|
||||
page: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface BaseSysParam {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 获得网页内容的参数值
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
html(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* list
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
html: string;
|
||||
info: string;
|
||||
page: string;
|
||||
add: string;
|
||||
list: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface BaseSysRole {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
info: string;
|
||||
list: string;
|
||||
page: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface BaseSysUser {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 移动部门
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
move(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
move: string;
|
||||
info: string;
|
||||
list: string;
|
||||
page: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface DemoGoods {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
info: string;
|
||||
page: string;
|
||||
list: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface DictInfo {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 获得字典数据
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
data(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
data: string;
|
||||
info: string;
|
||||
list: string;
|
||||
page: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface DictType {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
info: string;
|
||||
list: string;
|
||||
page: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface SpaceInfo {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
info: string;
|
||||
list: string;
|
||||
page: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface SpaceType {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 列表查询
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
info: string;
|
||||
list: string;
|
||||
page: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface TaskInfo {
|
||||
/**
|
||||
* 删除
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* 修改
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* 开始
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
start(data?: any): Promise<any>;
|
||||
/**
|
||||
* 执行一次
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
once(data?: any): Promise<any>;
|
||||
/**
|
||||
* 停止
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
stop(data?: any): Promise<any>;
|
||||
/**
|
||||
* 单个信息
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* 分页查询
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* 日志
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
log(data?: any): Promise<any>;
|
||||
/**
|
||||
* 新增
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* list
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
delete: string;
|
||||
update: string;
|
||||
start: string;
|
||||
once: string;
|
||||
stop: string;
|
||||
info: string;
|
||||
page: string;
|
||||
log: string;
|
||||
add: string;
|
||||
list: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface ChatMessage {
|
||||
/**
|
||||
* list
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* page
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* info
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* update
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* delete
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* add
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
list: string;
|
||||
page: string;
|
||||
info: string;
|
||||
update: string;
|
||||
delete: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface ChatSession {
|
||||
/**
|
||||
* list
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* page
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* info
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* update
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* delete
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* add
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
list: string;
|
||||
page: string;
|
||||
info: string;
|
||||
update: string;
|
||||
delete: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare interface Test {
|
||||
/**
|
||||
* list
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
list(data?: any): Promise<any>;
|
||||
/**
|
||||
* page
|
||||
* @returns Promise<PageResponse>
|
||||
*/
|
||||
page(data?: any): Promise<PageResponse>;
|
||||
/**
|
||||
* info
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
info(data?: any): Promise<any>;
|
||||
/**
|
||||
* update
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
update(data?: any): Promise<any>;
|
||||
/**
|
||||
* delete
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
delete(data?: any): Promise<any>;
|
||||
/**
|
||||
* add
|
||||
* @returns Promise<any>
|
||||
*/
|
||||
add(data?: any): Promise<any>;
|
||||
/**
|
||||
* 权限
|
||||
*/
|
||||
permission: {
|
||||
list: string;
|
||||
page: string;
|
||||
info: string;
|
||||
update: string;
|
||||
delete: string;
|
||||
add: string;
|
||||
};
|
||||
}
|
||||
|
||||
declare type Service = {
|
||||
request(data: RequestOptions): Promise<any>;
|
||||
base: {
|
||||
comm: BaseComm;
|
||||
open: BaseOpen;
|
||||
sys: {
|
||||
department: BaseSysDepartment;
|
||||
log: BaseSysLog;
|
||||
menu: BaseSysMenu;
|
||||
param: BaseSysParam;
|
||||
role: BaseSysRole;
|
||||
user: BaseSysUser;
|
||||
};
|
||||
};
|
||||
demo: { goods: DemoGoods };
|
||||
dict: { info: DictInfo; type: DictType };
|
||||
space: { info: SpaceInfo; type: SpaceType };
|
||||
task: { info: TaskInfo };
|
||||
chat: { message: ChatMessage; session: ChatSession };
|
||||
test: Test;
|
||||
};
|
|
@ -1,4 +1,5 @@
|
|||
import fs from "fs";
|
||||
import { isAbsolute, join, relative, sep } from "path";
|
||||
|
||||
// 首字母大写
|
||||
export function firstUpperCase(value: string): string {
|
||||
|
@ -44,3 +45,24 @@ export function parseJson(req: any) {
|
|||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 深度创建目录
|
||||
export function mkdirs(path: string) {
|
||||
const arr = path.split(sep);
|
||||
let p = "";
|
||||
|
||||
arr.forEach((e) => {
|
||||
try {
|
||||
fs.statSync(join(p, e));
|
||||
} catch (err) {
|
||||
try {
|
||||
fs.mkdirSync(join(p, e));
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
p = join(p, e);
|
||||
});
|
||||
|
||||
return p;
|
||||
}
|
||||
|
|
|
@ -1,106 +0,0 @@
|
|||
import { Plugin } from "vite";
|
||||
import { readFileSync, readdirSync, accessSync } from "fs";
|
||||
import path from "path";
|
||||
import { isArray } from "lodash";
|
||||
|
||||
let idPerfix = "";
|
||||
const svgTitle = /<svg([^>+].*?)>/;
|
||||
const clearHeightWidth = /(width|height)="([^>+].*?)"/g;
|
||||
|
||||
const hasViewBox = /(viewBox="[^>+].*?")/g;
|
||||
|
||||
const clearReturn = /(\r)|(\n)/g;
|
||||
|
||||
function findSvgFile(dir: string, uniqueNames: Record<string, boolean>): string[] {
|
||||
const svgRes = [];
|
||||
const dirents = readdirSync(dir, {
|
||||
withFileTypes: true
|
||||
});
|
||||
for (const dirent of dirents) {
|
||||
if (dirent.isDirectory()) {
|
||||
svgRes.push(...findSvgFile(path.join(dir, dirent.name), uniqueNames));
|
||||
} else if (uniqueNames[dirent.name]) {
|
||||
continue;
|
||||
} else {
|
||||
uniqueNames[dirent.name] = true;
|
||||
const svg = readFileSync(path.join(dir, dirent.name))
|
||||
.toString()
|
||||
.replace(clearReturn, "")
|
||||
.replace(svgTitle, (_: any, $2: any) => {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
let content = $2.replace(clearHeightWidth, (_: any, s2: any, s3: any) => {
|
||||
if (s2 === "width") {
|
||||
width = s3;
|
||||
} else if (s2 === "height") {
|
||||
height = s3;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
if (!hasViewBox.test($2)) {
|
||||
content += `viewBox="0 0 ${width} ${height}"`;
|
||||
}
|
||||
return `<symbol id="${idPerfix}-${dirent.name.replace(
|
||||
".svg",
|
||||
""
|
||||
)}" ${content}>`;
|
||||
})
|
||||
.replace("</svg>", "</symbol>");
|
||||
svgRes.push(svg);
|
||||
}
|
||||
}
|
||||
return svgRes;
|
||||
}
|
||||
|
||||
export const svgBuilder = (paths: string | string[], perfix = "icon"): Plugin | null => {
|
||||
if (paths) {
|
||||
idPerfix = perfix;
|
||||
paths = isArray(paths) ? paths : [paths];
|
||||
const uniqueNames: Record<string, boolean> = {};
|
||||
const res = paths.reduce(
|
||||
(previousValue, currentValue) =>
|
||||
previousValue.concat(findSvgFile(currentValue, uniqueNames)),
|
||||
[]
|
||||
);
|
||||
return {
|
||||
name: "svg-transform",
|
||||
transformIndexHtml(html): string {
|
||||
return html.replace(
|
||||
"<body>",
|
||||
`
|
||||
<body>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="position: absolute; width: 0; height: 0">
|
||||
${res.join("")}
|
||||
</svg>
|
||||
`
|
||||
);
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export const findSvgFolders = (dir: string): string[] => {
|
||||
const svgFolders = [];
|
||||
const dirents = readdirSync(dir, {
|
||||
withFileTypes: true
|
||||
});
|
||||
|
||||
// 找到结构为icons/svg的文件夹
|
||||
for (const dirent of dirents) {
|
||||
if (dirent.isDirectory()) {
|
||||
const testPath =
|
||||
dirent.name === "icons"
|
||||
? path.join(dir, "icons/svg")
|
||||
: path.join(dir, dirent.name, "icons/svg");
|
||||
try {
|
||||
accessSync(testPath);
|
||||
svgFolders.push(testPath);
|
||||
} catch (e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return svgFolders;
|
||||
};
|
24
index.html
|
@ -9,7 +9,7 @@
|
|||
name="viewport"
|
||||
content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"
|
||||
/>
|
||||
<title>COOL-ADMIN</title>
|
||||
<title></title>
|
||||
<link rel="icon" href="favicon.ico" />
|
||||
<style>
|
||||
html,
|
||||
|
@ -144,20 +144,20 @@
|
|||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div class="preload__wrap">
|
||||
<div class="preload__container">
|
||||
<p class="preload__name">COOL-ADMIN</p>
|
||||
<div class="preload__loading"></div>
|
||||
<p class="preload__title">正在加载资源...</p>
|
||||
<p class="preload__sub-title">初次加载资源可能需要较多时间 请耐心等待</p>
|
||||
</div>
|
||||
<div class="preload__wrap" id="Loading">
|
||||
<div class="preload__container">
|
||||
<p class="preload__name">COOL-ADMIN</p>
|
||||
<div class="preload__loading"></div>
|
||||
<p class="preload__title">正在加载资源...</p>
|
||||
<p class="preload__sub-title">初次加载资源可能需要较多时间 请耐心等待</p>
|
||||
</div>
|
||||
|
||||
<div class="preload__footer">
|
||||
<a href="https://cool-js.com/" target="_blank"> https://cool-js.com </a>
|
||||
</div>
|
||||
<div class="preload__footer">
|
||||
<a href="https://cool-js.com/" target="_blank"> https://cool-js.com </a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.ts"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
244
nginx.conf
|
@ -1,123 +1,123 @@
|
|||
user nginx;
|
||||
worker_processes 1;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
upstream backend {
|
||||
server midway:8001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location / {
|
||||
root /app;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
location /api/
|
||||
{
|
||||
proxy_pass http://backend/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
|
||||
#缓存相关配置
|
||||
#proxy_cache cache_one;
|
||||
#proxy_cache_key $host$request_uri$is_args$args;
|
||||
#proxy_cache_valid 200 304 301 302 1h;
|
||||
|
||||
#持久化连接相关配置
|
||||
proxy_connect_timeout 3000s;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 3000s;
|
||||
#proxy_http_version 1.1;
|
||||
#proxy_set_header Upgrade $http_upgrade;
|
||||
#proxy_set_header Connection "upgrade";
|
||||
|
||||
add_header X-Cache $upstream_cache_status;
|
||||
|
||||
#expires 12h;
|
||||
}
|
||||
# location /im {
|
||||
# proxy_pass http://backend/im;
|
||||
# proxy_connect_timeout 3600s; #配置点1
|
||||
# proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点
|
||||
# proxy_send_timeout 3600s; #配置点3
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header REMOTE-HOST $remote_addr;
|
||||
# #proxy_bind $remote_addr transparent;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection "upgrade";
|
||||
# # rewrite /socket/(.*) /$1 break;
|
||||
# proxy_redirect off;
|
||||
|
||||
# }
|
||||
|
||||
# location /socket {
|
||||
# proxy_pass http://backend/socket;
|
||||
# proxy_connect_timeout 3600s; #配置点1
|
||||
# proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点
|
||||
# proxy_send_timeout 3600s; #配置点3
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header REMOTE-HOST $remote_addr;
|
||||
# #proxy_bind $remote_addr transparent;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection "upgrade";
|
||||
# rewrite /socket/(.*) /$1 break;
|
||||
# proxy_redirect off;
|
||||
|
||||
# }
|
||||
|
||||
|
||||
location /adminer/
|
||||
{
|
||||
proxy_pass http://adminer:8080/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
|
||||
#缓存相关配置
|
||||
#proxy_cache cache_one;
|
||||
#proxy_cache_key $host$request_uri$is_args$args;
|
||||
#proxy_cache_valid 200 304 301 302 1h;
|
||||
|
||||
#持久化连接相关配置
|
||||
proxy_connect_timeout 3000s;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 3000s;
|
||||
#proxy_http_version 1.1;
|
||||
#proxy_set_header Upgrade $http_upgrade;
|
||||
#proxy_set_header Connection "upgrade";
|
||||
|
||||
add_header X-Cache $upstream_cache_status;
|
||||
|
||||
#expires 12h;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
}
|
||||
user nginx;
|
||||
worker_processes 1;
|
||||
error_log /var/log/nginx/error.log warn;
|
||||
pid /var/run/nginx.pid;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
http {
|
||||
include /etc/nginx/mime.types;
|
||||
default_type application/octet-stream;
|
||||
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
|
||||
'$status $body_bytes_sent "$http_referer" '
|
||||
'"$http_user_agent" "$http_x_forwarded_for"';
|
||||
access_log /var/log/nginx/access.log main;
|
||||
sendfile on;
|
||||
keepalive_timeout 65;
|
||||
upstream backend {
|
||||
server midway:8001;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
location / {
|
||||
root /app;
|
||||
index index.html;
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
location /api/
|
||||
{
|
||||
proxy_pass http://backend/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
|
||||
#缓存相关配置
|
||||
#proxy_cache cache_one;
|
||||
#proxy_cache_key $host$request_uri$is_args$args;
|
||||
#proxy_cache_valid 200 304 301 302 1h;
|
||||
|
||||
#持久化连接相关配置
|
||||
proxy_connect_timeout 3000s;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 3000s;
|
||||
#proxy_http_version 1.1;
|
||||
#proxy_set_header Upgrade $http_upgrade;
|
||||
#proxy_set_header Connection "upgrade";
|
||||
|
||||
add_header X-Cache $upstream_cache_status;
|
||||
|
||||
#expires 12h;
|
||||
}
|
||||
# location /im {
|
||||
# proxy_pass http://backend/im;
|
||||
# proxy_connect_timeout 3600s; #配置点1
|
||||
# proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点
|
||||
# proxy_send_timeout 3600s; #配置点3
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header REMOTE-HOST $remote_addr;
|
||||
# #proxy_bind $remote_addr transparent;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection "upgrade";
|
||||
# # rewrite /socket/(.*) /$1 break;
|
||||
# proxy_redirect off;
|
||||
|
||||
# }
|
||||
|
||||
# location /socket {
|
||||
# proxy_pass http://backend/socket;
|
||||
# proxy_connect_timeout 3600s; #配置点1
|
||||
# proxy_read_timeout 3600s; #配置点2,如果没效,可以考虑这个时间配置长一点
|
||||
# proxy_send_timeout 3600s; #配置点3
|
||||
# proxy_set_header Host $host;
|
||||
# proxy_set_header X-Real-IP $remote_addr;
|
||||
# proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
# proxy_set_header REMOTE-HOST $remote_addr;
|
||||
# #proxy_bind $remote_addr transparent;
|
||||
# proxy_http_version 1.1;
|
||||
# proxy_set_header Upgrade $http_upgrade;
|
||||
# proxy_set_header Connection "upgrade";
|
||||
# rewrite /socket/(.*) /$1 break;
|
||||
# proxy_redirect off;
|
||||
|
||||
# }
|
||||
|
||||
|
||||
location /adminer/
|
||||
{
|
||||
proxy_pass http://adminer:8080/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header REMOTE-HOST $remote_addr;
|
||||
|
||||
#缓存相关配置
|
||||
#proxy_cache cache_one;
|
||||
#proxy_cache_key $host$request_uri$is_args$args;
|
||||
#proxy_cache_valid 200 304 301 302 1h;
|
||||
|
||||
#持久化连接相关配置
|
||||
proxy_connect_timeout 3000s;
|
||||
proxy_read_timeout 86400s;
|
||||
proxy_send_timeout 3000s;
|
||||
#proxy_http_version 1.1;
|
||||
#proxy_set_header Upgrade $http_upgrade;
|
||||
#proxy_set_header Connection "upgrade";
|
||||
|
||||
add_header X-Cache $upstream_cache_status;
|
||||
|
||||
#expires 12h;
|
||||
}
|
||||
|
||||
error_page 500 502 503 504 /50x.html;
|
||||
location = /50x.html {
|
||||
root /usr/share/nginx/html;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
81
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "front-next",
|
||||
"version": "5.6.2",
|
||||
"version": "5.7.0",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
|
@ -9,63 +9,60 @@
|
|||
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cool-vue/crud": "^5.2.10",
|
||||
"@element-plus/icons-vue": "^1.1.3",
|
||||
"@vueuse/core": "^8.2.5",
|
||||
"@codemirror/lang-javascript": "^6.0.1",
|
||||
"@codemirror/theme-one-dark": "^6.0.0",
|
||||
"@cool-vue/crud": "^5.3.0",
|
||||
"@element-plus/icons-vue": "^2.0.6",
|
||||
"@vueuse/core": "^8.9.4",
|
||||
"axios": "^0.27.2",
|
||||
"codemirror": "^5.62.0",
|
||||
"core-js": "^3.6.5",
|
||||
"echarts": "^5.0.2",
|
||||
"element-plus": "^2.2.5",
|
||||
"codemirror": "^6.0.1",
|
||||
"core-js": "^3.23.5",
|
||||
"echarts": "^5.3.3",
|
||||
"element-plus": "^2.2.9",
|
||||
"file-saver": "^2.0.5",
|
||||
"js-beautify": "^1.13.5",
|
||||
"lodash": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.12",
|
||||
"pinia": "^2.0.16",
|
||||
"quill": "^1.3.7",
|
||||
"socket.io-client": "^4.5.1",
|
||||
"store": "^2.0.12",
|
||||
"unocss": "^0.31.0",
|
||||
"vue": "^3.2.32",
|
||||
"vue-echarts": "^6.0.2",
|
||||
"vue-router": "^4.0.14",
|
||||
"unocss": "^0.44.3",
|
||||
"vue": "^3.2.37",
|
||||
"vue-codemirror": "^6.0.0",
|
||||
"vue-echarts": "^6.2.3",
|
||||
"vue-router": "^4.1.2",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"xlsx": "^0.16.9"
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.14.168",
|
||||
"@types/node": "^16.10.2",
|
||||
"@types/lodash": "^4.14.182",
|
||||
"@types/mockjs": "^1.0.6",
|
||||
"@types/node": "^18.0.6",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/quill": "^2.0.9",
|
||||
"@types/store": "^2.0.2",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^4.20.0",
|
||||
"@typescript-eslint/parser": "^4.20.0",
|
||||
"@unocss/preset-uno": "^0.31.0",
|
||||
"@vitejs/plugin-vue": "^2.3.1",
|
||||
"@vitejs/plugin-vue-jsx": "^1.3.9",
|
||||
"@vue/cli-plugin-babel": "^5.0.1",
|
||||
"@vue/cli-plugin-typescript": "^5.0.1",
|
||||
"@vue/compiler-sfc": "^3.2.31",
|
||||
"@vue/composition-api": "^1.4.9",
|
||||
"eslint": "^7.23.0",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||
"@typescript-eslint/parser": "^5.30.6",
|
||||
"@unocss/preset-uno": "^0.44.3",
|
||||
"@vitejs/plugin-vue": "^3.0.1",
|
||||
"@vitejs/plugin-vue-jsx": "^2.0.0",
|
||||
"@vue/cli-plugin-babel": "^5.0.8",
|
||||
"@vue/cli-plugin-typescript": "^5.0.8",
|
||||
"@vue/compiler-sfc": "^3.2.37",
|
||||
"@vue/composition-api": "^1.7.0",
|
||||
"eslint": "^8.20.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.4.1",
|
||||
"sass": "^1.49.9",
|
||||
"sass-loader": "^11.1.1",
|
||||
"svg-sprite-loader": "^6.0.2",
|
||||
"typescript": "^4.6.2",
|
||||
"unplugin-vue-components": "^0.17.21",
|
||||
"vite": "^2.9.8",
|
||||
"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"
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.2.0",
|
||||
"magic-string": "^0.26.2",
|
||||
"prettier": "^2.7.1",
|
||||
"sass": "^1.53.0",
|
||||
"sass-loader": "^13.0.2",
|
||||
"typescript": "^4.7.4",
|
||||
"vite": "^3.0.2",
|
||||
"vite-plugin-compression": "^0.5.1"
|
||||
}
|
||||
}
|
||||
|
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
18
src/App.vue
|
@ -1,18 +1,5 @@
|
|||
<template>
|
||||
<el-config-provider :locale="zhCn">
|
||||
<div class="preload__wrap" v-if="app.loading">
|
||||
<div class="preload__container">
|
||||
<p class="preload__name">{{ app.info.name }}</p>
|
||||
<div class="preload__loading"></div>
|
||||
<p class="preload__title">正在加载菜单...</p>
|
||||
<p class="preload__sub-title">初次加载资源可能需要较多时间 请耐心等待</p>
|
||||
</div>
|
||||
|
||||
<div class="preload__footer">
|
||||
<a href="https://cool-js.com" target="_blank"> https://cool-js.com </a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
@ -20,9 +7,4 @@
|
|||
<script lang="ts" setup>
|
||||
import { ElConfigProvider } from "element-plus";
|
||||
import zhCn from "element-plus/lib/locale/lang/zh-cn";
|
||||
import { useBase } from "/$/base";
|
||||
|
||||
const { app } = useBase();
|
||||
</script>
|
||||
|
||||
<style lang="scss" src="./assets/css/index.scss"></style>
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
|
||||
"微软雅黑", Arial, sans-serif;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(144, 147, 153, 0.3);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:root {
|
||||
--view-bg-color: #f7f7f7;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input {
|
||||
&:-webkit-autofill {
|
||||
box-shadow: 0 0 0px 1000px white inset;
|
||||
}
|
||||
}
|
Before Width: | Height: | Size: 4.4 KiB |
|
@ -1,56 +1,36 @@
|
|||
import { createPinia } from "pinia";
|
||||
import { App } from "vue";
|
||||
import { useModule } from "./module";
|
||||
import { router, viewer } from "./router";
|
||||
import { modular } from "./module";
|
||||
import { router } from "./router";
|
||||
import { useBase } from "/$/base";
|
||||
import mitt from "mitt";
|
||||
import VueECharts from "vue-echarts";
|
||||
import ElementPlus from "element-plus";
|
||||
import "element-plus/theme-chalk/src/index.scss";
|
||||
import "uno.css";
|
||||
import { useDict } from "/$/dict";
|
||||
|
||||
export async function bootstrap(Vue: App) {
|
||||
// 缓存
|
||||
// pinia
|
||||
Vue.use(createPinia());
|
||||
|
||||
// ui库
|
||||
// element-plus
|
||||
Vue.use(ElementPlus);
|
||||
|
||||
// 事件通讯
|
||||
// mitt
|
||||
Vue.provide("mitt", mitt());
|
||||
|
||||
// 可视图表
|
||||
// charts
|
||||
Vue.component("v-chart", VueECharts);
|
||||
|
||||
// 基础
|
||||
const { app, user, menu } = useBase();
|
||||
|
||||
// 加载模块
|
||||
useModule(Vue);
|
||||
|
||||
// 取缓存视图
|
||||
viewer.add(menu.routes);
|
||||
|
||||
// 路由
|
||||
Vue.use(router);
|
||||
|
||||
// 开启
|
||||
app.showLoading();
|
||||
// 模块
|
||||
Vue.use(modular);
|
||||
|
||||
if (user.token) {
|
||||
// 字典
|
||||
const { dict } = useDict();
|
||||
// 数据
|
||||
const { app } = useBase();
|
||||
|
||||
// 获取字典数据
|
||||
dict.refresh();
|
||||
|
||||
// 获取用户信息
|
||||
user.get();
|
||||
|
||||
// 获取菜单权限
|
||||
await menu.get();
|
||||
}
|
||||
|
||||
app.hideLoading();
|
||||
// 事件加载
|
||||
app.req = modular.emit("onLoad");
|
||||
}
|
||||
|
|
|
@ -22,10 +22,8 @@ export const config = {
|
|||
router: {
|
||||
// 模式
|
||||
mode: "history",
|
||||
// 页面
|
||||
pages: [],
|
||||
// 视图 / 路由下的 children
|
||||
views: []
|
||||
// 首页组件
|
||||
home: import("/$/demo/views/home/index.vue")
|
||||
},
|
||||
|
||||
// 字体图标库
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
import { onBeforeUpdate, ref, inject, getCurrentInstance } from "vue";
|
||||
import { Emitter } from "mitt";
|
||||
import { onBeforeUpdate, ref, inject } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { useService } from "../service";
|
||||
|
||||
const service = useService();
|
||||
|
||||
export function useRefs() {
|
||||
const refs: any = ref<any[]>([]);
|
||||
|
||||
|
@ -16,34 +19,12 @@ export function useRefs() {
|
|||
return { refs, setRefs };
|
||||
}
|
||||
|
||||
// 服务
|
||||
const service = useService();
|
||||
|
||||
// 组件命名
|
||||
function named(name: string) {
|
||||
const { proxy }: any = getCurrentInstance();
|
||||
proxy.$.type.name = name;
|
||||
}
|
||||
|
||||
export function useCool() {
|
||||
const { refs, setRefs } = useRefs();
|
||||
|
||||
// 通信
|
||||
const mitt = inject<any>("mitt");
|
||||
|
||||
// 路由
|
||||
const route = useRoute();
|
||||
|
||||
// 路由器
|
||||
const router = useRouter();
|
||||
|
||||
return {
|
||||
route,
|
||||
router,
|
||||
refs,
|
||||
setRefs,
|
||||
service,
|
||||
mitt,
|
||||
named
|
||||
route: useRoute(),
|
||||
router: useRouter(),
|
||||
mitt: inject("mitt") as Emitter<any>,
|
||||
...useRefs()
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,4 +3,6 @@ export * from "./bootstrap";
|
|||
export * from "./hook";
|
||||
export * from "./router";
|
||||
export * from "./config";
|
||||
export { storage, module } from "./utils";
|
||||
export * from "./module";
|
||||
export * from "./types/index.d";
|
||||
export { storage, hideLoading } from "./utils";
|
||||
|
|
|
@ -1,162 +1,121 @@
|
|||
import { App } from "vue";
|
||||
import modules from "/@/modules";
|
||||
import { router, viewer } from "../router";
|
||||
import { filename, module } from "../utils";
|
||||
import { isFunction, isObject } from "lodash";
|
||||
import { isFunction, orderBy } from "lodash";
|
||||
import { Module } from "../types";
|
||||
import { filename } from "../utils";
|
||||
|
||||
// 扫描文件
|
||||
const files = import.meta.globEager("/src/modules/**/*");
|
||||
const files: any = import.meta.glob("/src/modules/*/{config.ts,service/**,directives/**}", {
|
||||
eager: true
|
||||
});
|
||||
|
||||
// 模块列表
|
||||
const list: any[] = [...modules];
|
||||
// @ts-ignore
|
||||
const list: Module[] = window.__modules__ || (window.__modules__ = []);
|
||||
|
||||
function main() {
|
||||
for (const i in files) {
|
||||
// 模块名
|
||||
const [, , , name, action] = i.split("/");
|
||||
// 模块
|
||||
const module = {
|
||||
list,
|
||||
|
||||
// 文件内容
|
||||
let value: any = null;
|
||||
get(name: string): Module {
|
||||
// @ts-ignore
|
||||
return this.list.find((e) => e.name == name);
|
||||
},
|
||||
|
||||
try {
|
||||
value = files[i].default;
|
||||
} catch (err) {
|
||||
console.error(err, i);
|
||||
value = files[i];
|
||||
}
|
||||
add(data: Module) {
|
||||
this.list.push(data);
|
||||
}
|
||||
};
|
||||
|
||||
if (!value) {
|
||||
continue;
|
||||
}
|
||||
// 解析
|
||||
for (const i in files) {
|
||||
// 分割
|
||||
const [, , , name, action] = i.split("/");
|
||||
|
||||
// 文件名
|
||||
const fname: string = filename(i);
|
||||
// 文件名
|
||||
const fname = filename(i);
|
||||
|
||||
// 文件内容
|
||||
const v = files[i]?.default;
|
||||
|
||||
// 模块是否存在
|
||||
const m = module.get(name);
|
||||
|
||||
// 数据
|
||||
const d = m || {
|
||||
name,
|
||||
value: null,
|
||||
services: [],
|
||||
directives: []
|
||||
};
|
||||
|
||||
switch (action) {
|
||||
// 配置参数
|
||||
function next(d: any) {
|
||||
// 配置参数入口
|
||||
if (action == "config.ts") {
|
||||
d.options = value || {};
|
||||
case "config.ts":
|
||||
d.value = v;
|
||||
break;
|
||||
|
||||
// 请求服务
|
||||
case "service":
|
||||
const s = new v();
|
||||
|
||||
if (s) {
|
||||
d.services?.push({
|
||||
path: s.namespace,
|
||||
value: s
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
// 模块入口
|
||||
if (action == "index.ts") {
|
||||
d.value = value || {};
|
||||
}
|
||||
|
||||
// 其他功能
|
||||
switch (action) {
|
||||
case "service":
|
||||
const s = new value();
|
||||
|
||||
d.service.push({
|
||||
path: s.namespace,
|
||||
value: s
|
||||
});
|
||||
break;
|
||||
|
||||
case "pages":
|
||||
case "views":
|
||||
if (value.cool) {
|
||||
d[action].push({
|
||||
...value.cool.route,
|
||||
component: value
|
||||
});
|
||||
}
|
||||
break;
|
||||
|
||||
case "components":
|
||||
d.components[value.name] = value;
|
||||
break;
|
||||
|
||||
case "directives":
|
||||
d.directives[fname] = value;
|
||||
break;
|
||||
}
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
// 是否存在
|
||||
const item: any = list.find((e) => e.name === name);
|
||||
|
||||
if (item) {
|
||||
if (!item.isLoaded) {
|
||||
next(item);
|
||||
}
|
||||
} else {
|
||||
list.push(
|
||||
next({
|
||||
name,
|
||||
options: {},
|
||||
directives: {},
|
||||
components: {},
|
||||
pages: [],
|
||||
views: [],
|
||||
service: []
|
||||
})
|
||||
);
|
||||
}
|
||||
// 指令
|
||||
case "directives":
|
||||
d.directives?.push({ name: fname, value: v });
|
||||
break;
|
||||
}
|
||||
|
||||
module.set(list);
|
||||
if (!m) {
|
||||
module.add(d);
|
||||
}
|
||||
}
|
||||
|
||||
main();
|
||||
// 模块处理器
|
||||
const modular = {
|
||||
install(app: App) {
|
||||
module.list.forEach((e) => {
|
||||
const d = isFunction(e.value) ? e.value(app) : e.value;
|
||||
|
||||
export function useModule(app: App) {
|
||||
// 模块安装
|
||||
list.forEach((e: any) => {
|
||||
if (isObject(e.value)) {
|
||||
if (isFunction(e.value.install)) {
|
||||
Object.assign(e, e.value.install(app, e.options));
|
||||
} else {
|
||||
Object.assign(e, e.value);
|
||||
}
|
||||
}
|
||||
if (d) {
|
||||
Object.assign(e, d);
|
||||
|
||||
try {
|
||||
// 注册组件
|
||||
if (e.components) {
|
||||
for (const i in e.components) {
|
||||
if (e.components[i]) {
|
||||
if (e.components[i].cool?.global || i.indexOf("cl-") === 0) {
|
||||
app.component(e.components[i].name, e.components[i]);
|
||||
}
|
||||
}
|
||||
// 注册组件
|
||||
e.components?.forEach(async (c: any) => {
|
||||
const v = await (isFunction(c) ? c() : c);
|
||||
const n = v.default || v;
|
||||
app.component(n.name, n);
|
||||
});
|
||||
|
||||
// 注册指令
|
||||
e.directives?.forEach((v) => {
|
||||
app.directive(v.name, v.value);
|
||||
});
|
||||
|
||||
// 安装事件
|
||||
if (d.install) {
|
||||
d.install(app, d.options);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
async emit(name: "onLoad") {
|
||||
const list = orderBy(module.list, "order");
|
||||
const events = {};
|
||||
|
||||
// 注册指令
|
||||
if (e.directives) {
|
||||
for (const i in e.directives) {
|
||||
app.directive(i, e.directives[i]);
|
||||
}
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (list[i][name]) {
|
||||
// @ts-ignore
|
||||
const e = await list[i][name](events);
|
||||
Object.assign(events, e);
|
||||
}
|
||||
|
||||
// 注册页面
|
||||
if (e.pages) {
|
||||
e.pages.forEach((e: any) => {
|
||||
router.addRoute(e);
|
||||
});
|
||||
}
|
||||
|
||||
// 注册视图
|
||||
if (e.views) {
|
||||
e.views.forEach((e: any) => {
|
||||
if (!e.meta) {
|
||||
e.meta = {};
|
||||
}
|
||||
|
||||
if (e.path) {
|
||||
viewer.add([e]);
|
||||
} else {
|
||||
console.error(`[${name}-views]:缺少 path 参数`);
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(`模块 ${name} 异常`, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { module, modular };
|
||||
|
|
|
@ -1,82 +1,41 @@
|
|||
// @ts-nocheck
|
||||
import { ElMessage } from "element-plus";
|
||||
import {
|
||||
createRouter,
|
||||
createWebHashHistory,
|
||||
createWebHistory,
|
||||
NavigationGuardNext,
|
||||
RouteRecordRaw
|
||||
} from "vue-router";
|
||||
import { storage, config } from "/@/cool";
|
||||
import { createRouter, createWebHashHistory, createWebHistory, RouteRecordRaw } from "vue-router";
|
||||
import { config, Router, storage, module, hideLoading } from "/@/cool";
|
||||
import { isArray } from "lodash";
|
||||
import { useBase } from "/$/base";
|
||||
import { cloneDeep, isArray } from "lodash";
|
||||
|
||||
// 视图文件
|
||||
const views = import.meta.globEager("/src/**/views/**/*.vue");
|
||||
|
||||
for (const i in views) {
|
||||
views[i.slice(5)] = views[i];
|
||||
delete views[i];
|
||||
}
|
||||
// 扫描文件
|
||||
const files = import.meta.glob(["/src/modules/*/{views,pages}/**/*", "!**/components"]);
|
||||
|
||||
// 默认路由
|
||||
const routes: RouteRecordRaw[] = [
|
||||
{
|
||||
path: "/",
|
||||
name: "index",
|
||||
component: () => import("/$/base/pages/layout/index.vue"),
|
||||
component: () => import("/$/base/layout/index.vue"),
|
||||
children: [
|
||||
{
|
||||
path: "/",
|
||||
name: "数据统计",
|
||||
component: () => import("/@/views/home/index.vue")
|
||||
},
|
||||
...config.app.router.views
|
||||
path: "",
|
||||
name: "home",
|
||||
component: config.app.router.home
|
||||
}
|
||||
]
|
||||
},
|
||||
...config.app.router.pages,
|
||||
{
|
||||
path: "/:catchAll(.*)",
|
||||
name: "404",
|
||||
redirect: "/404"
|
||||
}
|
||||
];
|
||||
|
||||
// 创建
|
||||
// 创建路由器
|
||||
const router = createRouter({
|
||||
history: config.app.router.mode == "history" ? createWebHistory() : createWebHashHistory(),
|
||||
routes
|
||||
}) as CoolRouter;
|
||||
}) as Router;
|
||||
|
||||
// 路由守卫
|
||||
router.beforeEach((to: any, _: any, next: NavigationGuardNext) => {
|
||||
const { user, process } = useBase();
|
||||
|
||||
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 (!config.ignore.token.find((e: string) => to.path == e)) {
|
||||
return next("/login");
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
// 组件加载后
|
||||
router.beforeResolve(() => {
|
||||
hideLoading();
|
||||
});
|
||||
|
||||
// 自定义
|
||||
router.href = function (path: string) {
|
||||
// 跳转
|
||||
router.href = function (path) {
|
||||
const url = import.meta.env.BASE_URL + path;
|
||||
|
||||
if (url != location.pathname) {
|
||||
|
@ -84,6 +43,44 @@ router.href = function (path: string) {
|
|||
}
|
||||
};
|
||||
|
||||
// 添加试图,页面路由
|
||||
router.append = function (data) {
|
||||
const list = isArray(data) ? data : [data];
|
||||
|
||||
list.forEach((e) => {
|
||||
const d = { ...e };
|
||||
|
||||
if (!d.name) {
|
||||
d.name = d.path.substring(1);
|
||||
}
|
||||
|
||||
if (e.isPage) {
|
||||
router.addRoute(d);
|
||||
} else {
|
||||
if (!d.component) {
|
||||
const url = d.viewPath;
|
||||
|
||||
if (url) {
|
||||
if (url.indexOf("http") == 0) {
|
||||
if (d.meta) {
|
||||
d.meta.iframeUrl = url;
|
||||
}
|
||||
|
||||
d.component = () => import(`/$/base/views/iframe/index.vue`);
|
||||
} else {
|
||||
d.component = files["/src/" + url.replace("cool/", "")];
|
||||
}
|
||||
} else {
|
||||
d.redirect = "/404";
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
router.addRoute("index", d);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
let lock = false;
|
||||
|
||||
// 错误监听
|
||||
|
@ -100,45 +97,93 @@ router.onError((err: any) => {
|
|||
}
|
||||
});
|
||||
|
||||
// 视图
|
||||
const viewer = {
|
||||
add(data: any[] | any) {
|
||||
// 列表
|
||||
const list = isArray(data) ? data : [data];
|
||||
// 注册
|
||||
async function register(path: string) {
|
||||
// 当前路由是否存在
|
||||
const d = router.getRoutes().find((e) => e.path == path);
|
||||
|
||||
list.forEach((e: any) => {
|
||||
const d: any = cloneDeep(e);
|
||||
if (!d) {
|
||||
const { app, menu } = useBase();
|
||||
|
||||
// 命名
|
||||
d.name = d.router;
|
||||
// 等待加载
|
||||
await app.req;
|
||||
|
||||
if (!d.component) {
|
||||
const url = d.viewPath;
|
||||
// 待注册列表
|
||||
const list: any[] = [];
|
||||
|
||||
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";
|
||||
}
|
||||
// 菜单数据
|
||||
menu.routes.find((e) => {
|
||||
list.push({
|
||||
...e,
|
||||
isPage: e.viewPath?.includes("/pages")
|
||||
});
|
||||
});
|
||||
|
||||
// 模块数据
|
||||
module.list.forEach((e) => {
|
||||
if (e.views) {
|
||||
list.push(...e.views);
|
||||
}
|
||||
|
||||
// 批量添加
|
||||
router.addRoute("index", d);
|
||||
if (e.pages) {
|
||||
list.push(
|
||||
...e.pages.map((d) => {
|
||||
return {
|
||||
...d,
|
||||
isPage: true
|
||||
};
|
||||
})
|
||||
);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
get() {
|
||||
return router.getRoutes().find((e) => e.name == "index")?.children;
|
||||
// 需要注册的路由
|
||||
const r = list.find((e) => e.path == path);
|
||||
|
||||
if (r) {
|
||||
router.append(r);
|
||||
}
|
||||
|
||||
return r?.path || "/404";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export { router, viewer };
|
||||
// 路由守卫
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 注册路由
|
||||
const path = await register(to.path);
|
||||
|
||||
if (path) {
|
||||
// 重定向
|
||||
next({ ...to, path });
|
||||
} else {
|
||||
// 数据缓存
|
||||
const { user, process } = useBase();
|
||||
|
||||
// 登录成功
|
||||
if (user.token) {
|
||||
// 在登录页
|
||||
if (to.path.includes("/login")) {
|
||||
// Token 未过期
|
||||
if (!storage.isExpired("token")) {
|
||||
// 回到首页
|
||||
return next("/");
|
||||
}
|
||||
} else {
|
||||
// 添加路由进程
|
||||
process.add(to);
|
||||
}
|
||||
} else {
|
||||
// 忽略部分 Token 验证
|
||||
if (!config.ignore.token.find((e) => to.path == e)) {
|
||||
return next("/login");
|
||||
}
|
||||
}
|
||||
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
export { router };
|
||||
|
|
|
@ -46,15 +46,7 @@ export class BaseService {
|
|||
}
|
||||
}
|
||||
|
||||
request(
|
||||
options = {} as {
|
||||
params?: any;
|
||||
data?: any;
|
||||
url: string;
|
||||
method?: "GET" | "get" | "POST" | "post" | string;
|
||||
[key: string]: any;
|
||||
}
|
||||
) {
|
||||
request(options: any = {}) {
|
||||
if (!options.params) options.params = {};
|
||||
|
||||
let ns = "";
|
||||
|
@ -91,7 +83,7 @@ export class BaseService {
|
|||
});
|
||||
}
|
||||
|
||||
page(data: { page?: number; size?: number; [key: string]: any }) {
|
||||
page(data: any) {
|
||||
return this.request({
|
||||
url: "/page",
|
||||
method: "POST",
|
||||
|
@ -99,14 +91,14 @@ export class BaseService {
|
|||
});
|
||||
}
|
||||
|
||||
info(params: { id?: number | string; [key: string]: any }) {
|
||||
info(params: any) {
|
||||
return this.request({
|
||||
url: "/info",
|
||||
params
|
||||
});
|
||||
}
|
||||
|
||||
update(data: { id?: number | string; [key: string]: any }) {
|
||||
update(data: any) {
|
||||
return this.request({
|
||||
url: "/update",
|
||||
method: "POST",
|
||||
|
@ -114,7 +106,7 @@ export class BaseService {
|
|||
});
|
||||
}
|
||||
|
||||
delete(data: { ids?: number[] | string[]; [key: string]: any }) {
|
||||
delete(data: any) {
|
||||
return this.request({
|
||||
url: "/delete",
|
||||
method: "POST",
|
||||
|
|
|
@ -13,7 +13,7 @@ function getNames(v: any) {
|
|||
// 标签名
|
||||
const names = getNames(new BaseService());
|
||||
|
||||
export function useEps(service: Service) {
|
||||
export function useEps(service: Eps.Service) {
|
||||
// 创建描述文件
|
||||
function createDts(list: any[]) {
|
||||
function deep(v: any) {
|
||||
|
|
|
@ -1,9 +1,35 @@
|
|||
import { deepFiles, deepMerge, module } from "../utils";
|
||||
import { deepMerge, basename } from "../utils";
|
||||
import { BaseService } from "./base";
|
||||
import { useEps } from "./eps";
|
||||
import { module } from "../module";
|
||||
|
||||
// 路径转对象
|
||||
function deepFiles(list: any[]) {
|
||||
const data: any = {};
|
||||
|
||||
list.forEach(({ path, value }) => {
|
||||
const arr: string[] = path.split("/");
|
||||
const parents = arr.slice(0, arr.length - 1);
|
||||
const name = basename(path).replace(".ts", "");
|
||||
|
||||
let curr = data;
|
||||
|
||||
parents.forEach((k) => {
|
||||
if (!curr[k]) {
|
||||
curr[k] = {};
|
||||
}
|
||||
|
||||
curr = curr[k];
|
||||
});
|
||||
|
||||
curr[name] = value;
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// 基础服务
|
||||
export const service: Service = {
|
||||
export const service: Eps.Service = {
|
||||
request: new BaseService().request
|
||||
};
|
||||
|
||||
|
@ -13,7 +39,7 @@ export function useService() {
|
|||
|
||||
// 模块内容
|
||||
module.list.forEach((e) => {
|
||||
deepMerge(service, deepFiles(e.service || []));
|
||||
deepMerge(service, deepFiles(e.services || []));
|
||||
});
|
||||
|
||||
return service;
|
||||
|
|
|
@ -15,22 +15,22 @@ NProgress.configure({
|
|||
});
|
||||
|
||||
// 请求队列
|
||||
let requests: Array<Function> = [];
|
||||
let requests: Array<(token: string) => void> = [];
|
||||
|
||||
// Token 是否刷新中
|
||||
// 是否刷新中
|
||||
let isRefreshing = false;
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-ignore 避免热更新后多次执行
|
||||
axios.interceptors.request.eject(axios._req);
|
||||
|
||||
// @ts-ignore
|
||||
// @ts-ignore 请求
|
||||
axios._req = axios.interceptors.request.use(
|
||||
(req: any) => {
|
||||
(req) => {
|
||||
const { user } = useBase();
|
||||
|
||||
if (req.url) {
|
||||
// 请求进度条
|
||||
if (!config.ignore.NProgress.some((e: string) => req.url.includes(e))) {
|
||||
if (!config.ignore.NProgress.some((e) => req.url?.includes(e))) {
|
||||
NProgress.start();
|
||||
}
|
||||
}
|
||||
|
@ -46,9 +46,11 @@ axios._req = axios.interceptors.request.use(
|
|||
// 验证 token
|
||||
if (user.token) {
|
||||
// 请求标识
|
||||
req.headers["Authorization"] = user.token;
|
||||
if (req.headers) {
|
||||
req.headers["Authorization"] = user.token;
|
||||
}
|
||||
|
||||
if (req.url.includes("refreshToken")) {
|
||||
if (req.url?.includes("refreshToken")) {
|
||||
return req;
|
||||
}
|
||||
|
||||
|
@ -64,7 +66,7 @@ axios._req = axios.interceptors.request.use(
|
|||
isRefreshing = true;
|
||||
|
||||
user.refreshToken()
|
||||
.then((token: string) => {
|
||||
.then((token) => {
|
||||
requests.forEach((cb) => cb(token));
|
||||
requests = [];
|
||||
isRefreshing = false;
|
||||
|
@ -76,9 +78,11 @@ axios._req = axios.interceptors.request.use(
|
|||
|
||||
return new Promise((resolve) => {
|
||||
// 继续请求
|
||||
requests.push((token: string) => {
|
||||
requests.push((token) => {
|
||||
// 重新设置 token
|
||||
req.headers["Authorization"] = token;
|
||||
if (req.headers) {
|
||||
req.headers["Authorization"] = token;
|
||||
}
|
||||
resolve(req);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
import { App, Component, Directive } from "vue";
|
||||
import { Router as VueRouter, RouteRecordRaw } from "vue-router";
|
||||
|
||||
export declare interface ModuleConfig {
|
||||
order?: number;
|
||||
options?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
components?: Component[];
|
||||
views?: RouteRecordRaw[];
|
||||
pages?: RouteRecordRaw[];
|
||||
install?(app: App, options?: { [key: string]: any }): void;
|
||||
onLoad?(events: {
|
||||
hasToken: (cb: () => Promise<any> | void) => Promise<any> | void;
|
||||
[key: string]: any;
|
||||
}): Promise<{ [key: string]: any }> | void;
|
||||
}
|
||||
|
||||
export declare interface Module extends ModuleConfig {
|
||||
name: string;
|
||||
options: {
|
||||
[key: string]: any;
|
||||
};
|
||||
value?: any;
|
||||
services?: { path: string; value: any }[];
|
||||
directives?: { name: string; value: Directive }[];
|
||||
}
|
||||
|
||||
export declare interface Router extends VueRouter {
|
||||
href(path: string): void;
|
||||
append(data: any[] | any): void;
|
||||
[key: string]: any;
|
||||
}
|
|
@ -1,6 +1,5 @@
|
|||
import { isArray, orderBy } from "lodash";
|
||||
import storage from "./storage";
|
||||
import module from "./module";
|
||||
|
||||
// 首字母大写
|
||||
export function firstUpperCase(value: string): string {
|
||||
|
@ -32,37 +31,35 @@ export function getUrlParam(name: string): string | null {
|
|||
return null;
|
||||
}
|
||||
|
||||
// 文件路径转对象
|
||||
export function deepFiles(list: any[]) {
|
||||
const modules: any = {};
|
||||
// 路径转数组
|
||||
export function deepPaths(paths: string[], splitor?: string) {
|
||||
const list: any[] = [];
|
||||
|
||||
list.forEach((e) => {
|
||||
const arr = e.path.split("/");
|
||||
const parents = arr.slice(0, arr.length - 1);
|
||||
const name = basename(e.path).replace(".ts", "");
|
||||
paths.forEach((e) => {
|
||||
const arr: string[] = e.split(splitor || "/").filter(Boolean);
|
||||
|
||||
let curr: any = modules;
|
||||
let prev: any = null;
|
||||
let key: any = null;
|
||||
let c = list;
|
||||
|
||||
parents.forEach((k: string) => {
|
||||
if (!curr[k]) {
|
||||
curr[k] = {};
|
||||
arr.forEach((a, i) => {
|
||||
let d = c.find((e) => e.label == a);
|
||||
|
||||
if (!d) {
|
||||
d = {
|
||||
label: a,
|
||||
value: a,
|
||||
children: arr[i + 1] ? [] : null
|
||||
};
|
||||
|
||||
c.push(d);
|
||||
}
|
||||
|
||||
prev = curr;
|
||||
curr = curr[k];
|
||||
key = k;
|
||||
if (d.children) {
|
||||
c = d.children;
|
||||
}
|
||||
});
|
||||
|
||||
if (name == "index") {
|
||||
prev[key] = e.value;
|
||||
} else {
|
||||
curr[name] = e.value;
|
||||
}
|
||||
});
|
||||
|
||||
return modules;
|
||||
return list;
|
||||
}
|
||||
|
||||
// 文件名
|
||||
|
@ -213,7 +210,7 @@ export function getBrowser() {
|
|||
|
||||
// 列表转树形
|
||||
export function deepTree(list: any[]): any[] {
|
||||
const newList: Array<any> = [];
|
||||
const newList: any[] = [];
|
||||
const map: any = {};
|
||||
|
||||
list.forEach((e) => (map[e.id] = e));
|
||||
|
@ -230,9 +227,8 @@ export function deepTree(list: any[]): any[] {
|
|||
|
||||
const fn = (list: Array<any>) => {
|
||||
list.map((e) => {
|
||||
if (e.children instanceof Array) {
|
||||
if (isArray(e.children)) {
|
||||
e.children = orderBy(e.children, "orderNum");
|
||||
|
||||
fn(e.children);
|
||||
}
|
||||
});
|
||||
|
@ -240,15 +236,15 @@ export function deepTree(list: any[]): any[] {
|
|||
|
||||
fn(newList);
|
||||
|
||||
return orderBy(newList, "orderNum");
|
||||
return orderBy(newList, "orderNum").filter((e) => !e.parentId);
|
||||
}
|
||||
|
||||
// 树形转列表
|
||||
export function revDeepTree(list: Array<any> = []) {
|
||||
const d: Array<any> = [];
|
||||
export function revDeepTree(list: any[]) {
|
||||
const arr: any[] = [];
|
||||
let id = 0;
|
||||
|
||||
const deep = (list: Array<any>, parentId: any) => {
|
||||
function deep(list: any[], parentId: any) {
|
||||
list.forEach((e) => {
|
||||
if (!e.id) {
|
||||
e.id = id++;
|
||||
|
@ -256,17 +252,25 @@ export function revDeepTree(list: Array<any> = []) {
|
|||
|
||||
e.parentId = parentId;
|
||||
|
||||
d.push(e);
|
||||
arr.push(e);
|
||||
|
||||
if (e.children && isArray(e.children)) {
|
||||
deep(e.children, e.id);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
deep(list || [], null);
|
||||
|
||||
return d;
|
||||
return arr;
|
||||
}
|
||||
|
||||
export { storage, module };
|
||||
export function hideLoading() {
|
||||
const el = document.getElementById("Loading");
|
||||
|
||||
if (el) {
|
||||
el.style.display = "none";
|
||||
}
|
||||
}
|
||||
|
||||
export { storage };
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// @ts-nocheck
|
||||
|
||||
interface Item {
|
||||
name: string;
|
||||
options: {
|
||||
[key: string]: any;
|
||||
};
|
||||
value: any;
|
||||
service?: any[];
|
||||
pages?: any[];
|
||||
views?: any[];
|
||||
components?: {
|
||||
[key: string]: any;
|
||||
};
|
||||
}
|
||||
|
||||
const module = {
|
||||
get list(): Item[] {
|
||||
return window.__modules__ || [];
|
||||
},
|
||||
|
||||
set(list: Item[]) {
|
||||
window.__modules__ = list;
|
||||
},
|
||||
|
||||
get(name: string) {
|
||||
return name ? window.__modules__.find((e) => e.name == name) : window.__modules__;
|
||||
}
|
||||
};
|
||||
|
||||
export default module;
|
|
@ -1,5 +1,4 @@
|
|||
/// <reference types="@cool-vue/crud" />
|
||||
/// <reference types="../build/cool/temp/service" />
|
||||
/// <reference types="../build/cool/temp/entity" />
|
||||
/// <reference types="../build/cool/temp/eps" />
|
||||
|
||||
declare const __EPS__: string;
|
||||
|
|
|
@ -2,9 +2,6 @@ import { createApp } from "vue";
|
|||
import App from "./App.vue";
|
||||
import { bootstrap } from "./cool";
|
||||
|
||||
// mock
|
||||
// import "./mock";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 启动
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
// @ts-nocheck
|
||||
const xhr = new window._XMLHttpRequest();
|
||||
window.XMLHttpRequest.prototype.upload = xhr.upload;
|
|
@ -1,4 +1,8 @@
|
|||
import "./resize";
|
||||
import { resize } from "./resize";
|
||||
|
||||
window.onload = function () {
|
||||
resize();
|
||||
};
|
||||
|
||||
export * from "./theme";
|
||||
export * from "./permission";
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useStore } from "../store";
|
||||
import { isArray, isObject } from "lodash";
|
||||
import { isObject } from "lodash";
|
||||
|
||||
function parse(value: any) {
|
||||
const { menu } = useStore();
|
||||
|
@ -11,7 +11,7 @@ function parse(value: any) {
|
|||
}
|
||||
}
|
||||
|
||||
export function checkPerm(value: any) {
|
||||
export function checkPerm(value: string | { or?: string[]; and?: string[] }) {
|
||||
if (!value) {
|
||||
return false;
|
||||
}
|
||||
|
@ -28,15 +28,3 @@ export function checkPerm(value: any) {
|
|||
|
||||
return parse(value);
|
||||
}
|
||||
|
||||
export function getPerm(service: any, names: string[] | string) {
|
||||
if (!service._permission) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!isArray(names)) {
|
||||
names = [names];
|
||||
}
|
||||
|
||||
return !names.find((e) => !service._permission[e]);
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { useEventListener } from "@vueuse/core";
|
||||
import { useStore } from "../store";
|
||||
|
||||
function resize() {
|
||||
function update() {
|
||||
const { app } = useStore();
|
||||
app.setBrowser();
|
||||
app.isFold = app.browser.isMini;
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
useEventListener(window, "resize", resize);
|
||||
resize();
|
||||
};
|
||||
export function resize() {
|
||||
useEventListener(window, "resize", update);
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -13,7 +13,9 @@ if (config.app.iconfont) {
|
|||
createLink("//at.alicdn.com/t/font_3254019_60a2xxj8uus.css");
|
||||
|
||||
// svg 图标加载
|
||||
const svgFiles = import.meta.globEager("/src/icons/svg/**/*.svg");
|
||||
const svgFiles = import.meta.glob("/src/modules/*/static/**/*.svg", {
|
||||
eager: true
|
||||
});
|
||||
|
||||
function iconList() {
|
||||
const list: string[] = [];
|
||||
|
|
|
@ -1,142 +1,86 @@
|
|||
<template>
|
||||
<div ref="editorRef" class="cl-codemirror">
|
||||
<textarea id="editor" class="cl-code" :height="height" :width="width"></textarea>
|
||||
|
||||
<div class="cl-codemirror__tools">
|
||||
<el-button @click="formatCode">格式化</el-button>
|
||||
</div>
|
||||
<div class="cl-codemirror">
|
||||
<codemirror
|
||||
v-model="content"
|
||||
:placeholder="placeholder"
|
||||
:style="{ height, fontSize }"
|
||||
autofocus
|
||||
indent-with-tab
|
||||
:tab-size="4"
|
||||
:extensions="extensions"
|
||||
@change="onChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, nextTick, onMounted, ref, watch } from "vue";
|
||||
import CodeMirror from "codemirror";
|
||||
import beautifyJs from "js-beautify";
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import "codemirror/addon/hint/show-hint.css";
|
||||
import "codemirror/theme/hopscotch.css";
|
||||
import "codemirror/addon/hint/javascript-hint";
|
||||
import "codemirror/mode/javascript/javascript";
|
||||
import { deepMerge } from "/@/cool/utils";
|
||||
<script lang="ts" name="cl-codemirror" setup>
|
||||
import { Codemirror } from "vue-codemirror";
|
||||
import { javascript } from "@codemirror/lang-javascript";
|
||||
import { oneDark } from "@codemirror/theme-one-dark";
|
||||
import { ref, watch } from "vue";
|
||||
import { useDark } from "@vueuse/core";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-codemirror",
|
||||
|
||||
props: {
|
||||
modelValue: null,
|
||||
height: String,
|
||||
width: String,
|
||||
options: Object
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
|
||||
emits: ["update:modelValue", "load"],
|
||||
|
||||
setup(props, { emit }) {
|
||||
const editorRef = ref<any>(null);
|
||||
|
||||
let editor: any = null;
|
||||
|
||||
// 获取内容
|
||||
function getValue() {
|
||||
if (editor) {
|
||||
return editor.getValue();
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// 设置内容
|
||||
function setValue(val?: string) {
|
||||
if (editor) {
|
||||
editor.setValue(val || "");
|
||||
}
|
||||
}
|
||||
|
||||
// 格式化
|
||||
function formatCode() {
|
||||
if (editor) {
|
||||
editor.setValue(beautifyJs(getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
// 监听内容变化
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val: string) => {
|
||||
if (editor && val != getValue()) {
|
||||
setValue(val);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(function () {
|
||||
nextTick(() => {
|
||||
// 实例化
|
||||
editor = CodeMirror.fromTextArea(
|
||||
editorRef.value.querySelector("#editor"),
|
||||
deepMerge(
|
||||
{
|
||||
mode: "javascript",
|
||||
theme: "hopscotch",
|
||||
styleActiveLine: true,
|
||||
lineNumbers: true,
|
||||
lineWrapping: true,
|
||||
indentWithTabs: true,
|
||||
indentUnit: 4,
|
||||
extraKeys: { Ctrl: "autocomplete" },
|
||||
foldGutter: true,
|
||||
autofocus: true,
|
||||
matchBrackets: true,
|
||||
autoCloseBrackets: true,
|
||||
gutters: [
|
||||
"CodeMirror-linenumbers",
|
||||
"CodeMirror-foldgutter",
|
||||
"CodeMirror-lint-markers"
|
||||
]
|
||||
},
|
||||
props.options
|
||||
)
|
||||
);
|
||||
|
||||
// 输入监听
|
||||
editor.on("change", (e: any) => {
|
||||
emit("update:modelValue", e.getValue());
|
||||
});
|
||||
|
||||
// 设置内容
|
||||
setValue(props.modelValue);
|
||||
|
||||
// 格式化
|
||||
formatCode();
|
||||
|
||||
// 加载回调
|
||||
emit("load", editor);
|
||||
|
||||
// 设置编辑框大小
|
||||
editor.setSize(props.width || "auto", props.height || "auto");
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
editorRef,
|
||||
formatCode
|
||||
};
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请输入"
|
||||
},
|
||||
height: {
|
||||
type: String,
|
||||
default: "400px"
|
||||
},
|
||||
fontSize: {
|
||||
type: String,
|
||||
default: "14px"
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue", "change"]);
|
||||
|
||||
// 是否暗黑模式
|
||||
const isDark = ref(useDark());
|
||||
|
||||
// 插件
|
||||
const extensions: any[] = [javascript(), isDark.value && oneDark];
|
||||
|
||||
// 内容
|
||||
const content = ref("");
|
||||
|
||||
// 值改变
|
||||
function onChange(value: string) {
|
||||
emit("update:modelValue", value);
|
||||
emit("change", value);
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
content.value = val;
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
<style lang="scss" scoped>
|
||||
.cl-codemirror {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #dcdfe6;
|
||||
border: 1px solid var(--el-border-color);
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
line-height: 150%;
|
||||
|
||||
&__tools {
|
||||
background-color: #322931;
|
||||
padding: 10px;
|
||||
border-top: 1px solid #444;
|
||||
:deep(.cm-editor) {
|
||||
.cm-foldGutter {
|
||||
width: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.cm-focused {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -4,50 +4,34 @@
|
|||
</svg>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref } from "vue";
|
||||
<script lang="ts" name="cl-svg" setup>
|
||||
import { computed, ref } from "vue";
|
||||
import { isNumber } from "lodash";
|
||||
|
||||
export default defineComponent({
|
||||
name: "icon-svg",
|
||||
|
||||
cool: {
|
||||
global: true
|
||||
const props = defineProps({
|
||||
name: {
|
||||
type: String
|
||||
},
|
||||
|
||||
props: {
|
||||
name: {
|
||||
type: String
|
||||
},
|
||||
className: {
|
||||
type: String
|
||||
},
|
||||
size: {
|
||||
type: [String, Number]
|
||||
}
|
||||
className: {
|
||||
type: String
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
const style = ref<any>({
|
||||
fontSize: isNumber(props.size) ? props.size + "px" : props.size
|
||||
});
|
||||
|
||||
const iconName = computed<string>(() => `#icon-${props.name}`);
|
||||
const svgClass = computed<Array<string>>(() => {
|
||||
return ["icon-svg", `icon-svg__${props.name}`, String(props.className || "")];
|
||||
});
|
||||
|
||||
return {
|
||||
style,
|
||||
iconName,
|
||||
svgClass
|
||||
};
|
||||
size: {
|
||||
type: [String, Number]
|
||||
}
|
||||
});
|
||||
|
||||
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 || "")];
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.icon-svg {
|
||||
<style lang="scss" scoped>
|
||||
.cl-svg {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
|
|
|
@ -45,10 +45,7 @@ export default defineComponent({
|
|||
type: [Number, Array],
|
||||
default: 100
|
||||
},
|
||||
lazy: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
lazy: Boolean,
|
||||
fit: {
|
||||
type: String,
|
||||
default: "cover"
|
||||
|
|
|
@ -25,56 +25,45 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, watch } from "vue";
|
||||
<script lang="ts" name="cl-view-group" setup>
|
||||
import { ref, watch } from "vue";
|
||||
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
|
||||
import { useBase } from "/$/base";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-view-group",
|
||||
components: {
|
||||
ArrowLeft,
|
||||
ArrowRight
|
||||
},
|
||||
props: {
|
||||
title: String
|
||||
},
|
||||
setup() {
|
||||
const { app } = useBase();
|
||||
defineProps({
|
||||
title: String
|
||||
});
|
||||
|
||||
// 是否展开
|
||||
const isExpand = ref<boolean>(true);
|
||||
const { app } = useBase();
|
||||
|
||||
// 收起、展开
|
||||
function toExpand(value?: boolean) {
|
||||
isExpand.value = value === undefined ? !isExpand.value : value;
|
||||
}
|
||||
// 是否展开
|
||||
const isExpand = ref<boolean>(true);
|
||||
|
||||
// 小屏幕下
|
||||
function checkExpand(value?: boolean) {
|
||||
if (app.browser.isMini) {
|
||||
toExpand(value);
|
||||
}
|
||||
}
|
||||
// 收起、展开
|
||||
function toExpand(value?: boolean) {
|
||||
isExpand.value = value === undefined ? !isExpand.value : value;
|
||||
}
|
||||
|
||||
// 监听屏幕大小变化
|
||||
watch(
|
||||
() => app.browser.isMini,
|
||||
(val: boolean) => {
|
||||
isExpand.value = !val;
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
isExpand,
|
||||
toExpand,
|
||||
checkExpand,
|
||||
app
|
||||
};
|
||||
// 小屏幕下
|
||||
function checkExpand(value?: boolean) {
|
||||
if (app.browser.isMini) {
|
||||
toExpand(value);
|
||||
}
|
||||
}
|
||||
|
||||
// 监听屏幕大小变化
|
||||
watch(
|
||||
() => app.browser.isMini,
|
||||
(val: boolean) => {
|
||||
isExpand.value = !val;
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
defineExpose({
|
||||
checkExpand
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -88,17 +77,18 @@ export default defineComponent({
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background-color: var(--el-bg-color);
|
||||
}
|
||||
|
||||
&__left {
|
||||
height: 100%;
|
||||
width: 300px;
|
||||
max-width: calc(100% - 50px);
|
||||
background-color: #fff;
|
||||
transition: width 0.3s;
|
||||
margin-right: 10px;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
border-right: 1px solid var(--el-border-color);
|
||||
box-sizing: border-box;
|
||||
|
||||
&._collapse {
|
||||
margin-right: 0;
|
||||
|
@ -117,7 +107,6 @@ export default defineComponent({
|
|||
justify-content: center;
|
||||
height: 40px;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
|
@ -133,7 +122,6 @@ export default defineComponent({
|
|||
top: 0;
|
||||
font-size: 18px;
|
||||
cursor: pointer;
|
||||
background-color: #fff;
|
||||
height: 40px;
|
||||
width: 80px;
|
||||
padding-left: 10px;
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
import { ModuleConfig, config } from "/@/cool";
|
||||
import { useStore } from "./store";
|
||||
import "./static/css/index.scss";
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
order: 99,
|
||||
components: Object.values(import.meta.glob("./components/**/*")),
|
||||
views: [
|
||||
{
|
||||
path: "/my/info",
|
||||
meta: {
|
||||
label: "个人中心"
|
||||
},
|
||||
component: () => import("./views/info.vue")
|
||||
}
|
||||
],
|
||||
pages: [
|
||||
{
|
||||
path: "/login",
|
||||
component: () => import("./pages/login/index.vue")
|
||||
},
|
||||
{
|
||||
path: "/401",
|
||||
meta: {
|
||||
process: false
|
||||
},
|
||||
component: () => import("./pages/error-page/401.vue")
|
||||
},
|
||||
{
|
||||
path: "/403",
|
||||
meta: {
|
||||
process: false
|
||||
},
|
||||
component: () => import("./pages/error-page/403.vue")
|
||||
},
|
||||
{
|
||||
path: "/404",
|
||||
meta: {
|
||||
process: false
|
||||
},
|
||||
component: () => import("./pages/error-page/404.vue")
|
||||
},
|
||||
{
|
||||
path: "/500",
|
||||
meta: {
|
||||
process: false
|
||||
},
|
||||
component: () => import("./pages/error-page/500.vue")
|
||||
},
|
||||
{
|
||||
path: "/502",
|
||||
meta: {
|
||||
process: false
|
||||
},
|
||||
component: () => import("./pages/error-page/502.vue")
|
||||
}
|
||||
],
|
||||
install() {
|
||||
// 设置标题
|
||||
document.title = config.app.name;
|
||||
},
|
||||
async onLoad() {
|
||||
const { user, menu } = useStore();
|
||||
|
||||
if (user.token) {
|
||||
// 获取用户信息
|
||||
user.get();
|
||||
// 获取菜单权限
|
||||
await menu.get();
|
||||
}
|
||||
|
||||
return {
|
||||
async hasToken(cb: () => Promise<any> | void) {
|
||||
if (user.token) {
|
||||
if (cb) await cb();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
|
@ -5,7 +5,7 @@ function change(el: any, binding: any) {
|
|||
}
|
||||
|
||||
export default {
|
||||
beforeMount(el: any, binding: any) {
|
||||
created(el: any, binding: any) {
|
||||
el.setAttribute("_display", el.style.display || "");
|
||||
change(el, binding);
|
||||
},
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { useStore } from "./store";
|
||||
import "./static/css/index.scss";
|
||||
|
||||
export function useBase() {
|
||||
return {
|
||||
|
@ -8,3 +7,4 @@ export function useBase() {
|
|||
}
|
||||
|
||||
export * from "./common";
|
||||
export * from "./types/index.d";
|
||||
|
|
|
@ -7,20 +7,19 @@
|
|||
@select="select"
|
||||
>
|
||||
<el-menu-item v-for="(item, index) in menu.group" :key="index" :index="`${index}`">
|
||||
<icon-svg v-if="item.icon" :name="item.icon" :size="18" />
|
||||
<cl-svg v-if="item.icon" :name="item.icon" :size="18" />
|
||||
<span class="a-menu__name">{{ item.name }}</span>
|
||||
</el-menu-item>
|
||||
</el-menu>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts" name="a-menu" setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useBase } from "/$/base";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { router, route } = useCool();
|
||||
|
||||
const { menu } = useBase();
|
||||
|
||||
// 选中标识
|
||||
|
@ -101,7 +100,7 @@ onMounted(function () {
|
|||
color: #000;
|
||||
}
|
||||
|
||||
.icon-svg {
|
||||
.cl-svg {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
import { h, ref, watch } from "vue";
|
||||
import { useStore } from "../../store";
|
||||
import { Menu } from "../../types";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
export default {
|
||||
name: "b-menu",
|
||||
|
||||
setup() {
|
||||
const { router, route } = useCool();
|
||||
const { menu } = useStore();
|
||||
|
||||
// 是否可见
|
||||
const visible = ref(true);
|
||||
|
||||
// 页面跳转
|
||||
function toView(url: string) {
|
||||
if (url != route.path) {
|
||||
router.push(url);
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新菜单
|
||||
function refresh() {
|
||||
visible.value = false;
|
||||
|
||||
setTimeout(() => {
|
||||
visible.value = true;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// 监听菜单变化
|
||||
watch(menu.list, refresh);
|
||||
|
||||
return {
|
||||
route,
|
||||
visible,
|
||||
toView,
|
||||
refresh,
|
||||
menu
|
||||
};
|
||||
},
|
||||
|
||||
render(ctx: any) {
|
||||
const { app } = useStore();
|
||||
|
||||
// 设置子菜单
|
||||
function deep(list: Menu.Item[], index: number) {
|
||||
return list
|
||||
.filter((e) => e.isShow)
|
||||
.map((e) => {
|
||||
let html = null;
|
||||
|
||||
if (e.type == 0) {
|
||||
html = h(
|
||||
<el-sub-menu></el-sub-menu>,
|
||||
{
|
||||
index: String(e.id),
|
||||
key: e.id,
|
||||
popperClass: "app-slider__menu"
|
||||
},
|
||||
{
|
||||
title() {
|
||||
return (
|
||||
<div class="wrap">
|
||||
<cl-svg name={e.icon} />
|
||||
<span v-show={!app.isFold || index != 1}>{e.name}</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
default() {
|
||||
return deep(e.children || [], index + 1);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
html = h(
|
||||
<el-menu-item />,
|
||||
{
|
||||
index: e.path,
|
||||
key: e.id
|
||||
},
|
||||
{
|
||||
default() {
|
||||
return (
|
||||
<div class="wrap">
|
||||
<cl-svg name={e.icon} />
|
||||
<span v-show={!app.isFold || index != 1}>{e.name}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return html;
|
||||
});
|
||||
}
|
||||
|
||||
const children = deep(ctx.menu.list, 1);
|
||||
|
||||
return (
|
||||
ctx.visible && (
|
||||
<div class="app-slider__menu">
|
||||
<el-menu
|
||||
default-active={ctx.route.path}
|
||||
background-color="transparent"
|
||||
collapse-transition={false}
|
||||
collapse={app.browser.isMini ? false : app.isFold}
|
||||
onSelect={ctx.toView}
|
||||
>
|
||||
{children}
|
||||
</el-menu>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
};
|
|
@ -2,7 +2,7 @@
|
|||
<div class="app-process">
|
||||
<div class="app-process__back" @click="router.back">
|
||||
<el-icon :size="15"><arrow-left /></el-icon>
|
||||
<span>返回</span>
|
||||
<span>后退</span>
|
||||
</div>
|
||||
|
||||
<div :ref="setRefs('scroller')" class="app-process__scroller">
|
||||
|
@ -16,9 +16,9 @@
|
|||
@click="onTap(item, Number(index))"
|
||||
@contextmenu.stop.prevent="openCM($event, item)"
|
||||
>
|
||||
<span>{{ item.label }}</span>
|
||||
<el-icon v-if="index > 0" @mousedown.stop="onDel(Number(index))">
|
||||
<close />
|
||||
<span>{{ item.meta?.label || item.name || item.path }}</span>
|
||||
<el-icon @mousedown.stop="onDel(Number(index))">
|
||||
<Close />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -32,17 +32,18 @@ import { useCool } from "/@/cool";
|
|||
import { ArrowLeft, Close } from "@element-plus/icons-vue";
|
||||
import { ContextMenu } from "@cool-vue/crud";
|
||||
import { useBase } from "/$/base";
|
||||
import { Process } from "/$/base/types";
|
||||
|
||||
const { refs, setRefs, route, router } = useCool();
|
||||
const { process } = useBase();
|
||||
|
||||
// 跳转
|
||||
function toPath() {
|
||||
const active = process.list.find((e: any) => e.active);
|
||||
const d = process.list.find((e) => e.active);
|
||||
|
||||
if (!active) {
|
||||
if (!d) {
|
||||
const next = last(process.list);
|
||||
router.push(next ? next.value : "/");
|
||||
router.push(next ? next.fullPath : "/");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,9 +65,9 @@ function adScroll(index: number) {
|
|||
}
|
||||
|
||||
// 选择
|
||||
function onTap(item: any, index: number) {
|
||||
function onTap(item: Process.Item, index: number) {
|
||||
adScroll(index);
|
||||
router.push(item.value);
|
||||
router.push(item.fullPath);
|
||||
}
|
||||
|
||||
// 删除
|
||||
|
@ -76,14 +77,14 @@ function onDel(index: number) {
|
|||
}
|
||||
|
||||
// 右键菜单
|
||||
function openCM(e: any, item: any) {
|
||||
function openCM(e: any, item: Process.Item) {
|
||||
ContextMenu.open(e, {
|
||||
list: [
|
||||
{
|
||||
label: "关闭当前",
|
||||
hidden: item.value !== route.path,
|
||||
hidden: item.fullPath !== route.path,
|
||||
callback(done) {
|
||||
onDel(process.list.findIndex((e: any) => e.value == item.value));
|
||||
onDel(process.list.findIndex((e) => e.fullPath == item.fullPath));
|
||||
done();
|
||||
toPath();
|
||||
}
|
||||
|
@ -91,9 +92,7 @@ function openCM(e: any, item: any) {
|
|||
{
|
||||
label: "关闭其他",
|
||||
callback(done) {
|
||||
process.set(
|
||||
process.list.filter((e: any) => e.value == item.value || e.value == "/")
|
||||
);
|
||||
process.set(process.list.filter((e) => e.fullPath == item.fullPath));
|
||||
done();
|
||||
toPath();
|
||||
}
|
||||
|
@ -101,7 +100,7 @@ function openCM(e: any, item: any) {
|
|||
{
|
||||
label: "关闭所有",
|
||||
callback(done) {
|
||||
process.set(process.list.filter((e: any) => e.value == "/"));
|
||||
process.clear();
|
||||
done();
|
||||
toPath();
|
||||
}
|
||||
|
@ -113,7 +112,7 @@ function openCM(e: any, item: any) {
|
|||
watch(
|
||||
() => route.path,
|
||||
function (val) {
|
||||
adScroll(process.list.findIndex((e: any) => e.value === val) || 0);
|
||||
adScroll(process.list.findIndex((e) => e.fullPath === val) || 0);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
@ -138,6 +137,7 @@ watch(
|
|||
margin-right: 10px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
color: #000;
|
||||
|
||||
&:hover {
|
||||
background-color: #eee;
|
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<div class="route-nav">
|
||||
<p v-if="app.browser.isMini" class="title">
|
||||
{{ lastName }}
|
||||
</p>
|
||||
|
||||
<template v-else>
|
||||
<el-breadcrumb>
|
||||
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="(item, index) in list" :key="index">{{
|
||||
item.meta?.label || item.name
|
||||
}}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="route-nav" setup>
|
||||
import { computed } from "vue";
|
||||
import _ from "lodash";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useBase } from "/$/base";
|
||||
|
||||
const { route } = useCool();
|
||||
const { app, menu } = useBase();
|
||||
|
||||
// 数据列表
|
||||
const list = computed(() => {
|
||||
function deep(item: any) {
|
||||
if (route.path === "/") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.path == route.path) {
|
||||
return item;
|
||||
} else {
|
||||
if (item.children) {
|
||||
const ret = item.children.map(deep).find(Boolean);
|
||||
|
||||
if (ret) {
|
||||
return [item, ret];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _(menu.group).map(deep).filter(Boolean).flattenDeep().value();
|
||||
});
|
||||
|
||||
// 最后一个节点名称
|
||||
const lastName = computed(() => _.last(list.value).name);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.route-nav {
|
||||
white-space: nowrap;
|
||||
|
||||
.el-breadcrumb {
|
||||
margin: 0 10px;
|
||||
|
||||
&__inner {
|
||||
font-size: 13px;
|
||||
padding: 0 10px;
|
||||
font-weight: normal;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,131 @@
|
|||
<template>
|
||||
<div class="app-slider">
|
||||
<div class="app-slider__logo" @click="toHome">
|
||||
<img src="/logo.png" />
|
||||
<span v-if="!app.isFold || app.browser.isMini">{{ app.info.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="app-slider__container">
|
||||
<b-menu />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useBase } from "/$/base";
|
||||
import BMenu from "./bmenu";
|
||||
|
||||
const { app } = useBase();
|
||||
|
||||
function toHome() {
|
||||
location.href = "https://cool-js.com";
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.app-slider {
|
||||
height: 100%;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
background-color: #2f3447;
|
||||
|
||||
&__logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 80px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
font-size: 26px;
|
||||
margin-left: 10px;
|
||||
font-family: inherit;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
height: calc(100% - 80px);
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__menu {
|
||||
&.el-popper {
|
||||
&.is-light {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border-right: 0;
|
||||
background-color: transparent;
|
||||
|
||||
&--popup {
|
||||
.cl-svg,
|
||||
span {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu__title,
|
||||
&-item {
|
||||
&.is-active,
|
||||
&:hover {
|
||||
background-color: var(--color-primary) !important;
|
||||
|
||||
.cl-svg,
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu__title,
|
||||
&-item,
|
||||
&__title {
|
||||
color: #eee;
|
||||
letter-spacing: 0.5px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
|
||||
.wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cl-svg {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
margin-left: 10px;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
&--collapse {
|
||||
.wrap {
|
||||
text-align: center;
|
||||
|
||||
.cl-svg {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -30,7 +30,7 @@
|
|||
|
||||
<!-- 用户信息 -->
|
||||
<div class="app-topbar__user" v-if="user.info">
|
||||
<el-dropdown trigger="click" :hide-on-click="false" @command="onCommand">
|
||||
<el-dropdown trigger="click" hide-on-click @command="onCommand">
|
||||
<span class="el-dropdown-link">
|
||||
<span class="name">{{ user.info.nickName }}</span>
|
||||
<img class="avatar" :src="user.info.headImg" />
|
||||
|
@ -53,7 +53,7 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
<script lang="ts" name="topbar" setup>
|
||||
import { useBase } from "/$/base";
|
||||
import { useCool } from "/@/cool";
|
||||
import RouteNav from "./route-nav.vue";
|
||||
|
@ -117,7 +117,7 @@ function onCommand(name: string) {
|
|||
align-items: center;
|
||||
list-style: none;
|
||||
height: 45px;
|
||||
width: 45px;
|
||||
min-width: 45px;
|
||||
border-radius: 3px;
|
||||
cursor: pointer;
|
||||
margin-left: 10px;
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="app-views" v-if="!app.loading">
|
||||
<div class="app-views">
|
||||
<router-view v-slot="{ Component }">
|
||||
<keep-alive :include="caches">
|
||||
<component :is="Component" />
|
||||
|
@ -12,14 +12,14 @@
|
|||
import { computed } from "vue";
|
||||
import { useBase } from "/$/base";
|
||||
|
||||
const { app, process } = useBase();
|
||||
const { process } = useBase();
|
||||
|
||||
// 缓存列表
|
||||
const caches = computed(() => {
|
||||
return process.list
|
||||
.filter((e: any) => e.keepAlive)
|
||||
.map((e: any) => {
|
||||
return e.value.substring(1, e.value.length).replace(/\//g, "-");
|
||||
.filter((e) => e.meta?.keepAlive)
|
||||
.map((e) => {
|
||||
return e.path.substring(1, e.path.length).replace(/\//g, "-");
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
@ -28,10 +28,10 @@ const caches = computed(() => {
|
|||
.app-views {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 0 10px;
|
||||
margin: 0 10px;
|
||||
margin-bottom: 10px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
width: calc(100% - 20px);
|
||||
box-sizing: border-box;
|
||||
border-radius: 3px;
|
||||
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div class="page-layout" :class="{ collapse: app.isFold }">
|
||||
<div class="page-layout__mask" @click="app.fold(true)"></div>
|
||||
<div class="app-layout" :class="{ collapse: app.isFold }">
|
||||
<div class="app-layout__mask" @click="app.fold(true)"></div>
|
||||
|
||||
<div class="page-layout__left">
|
||||
<div class="app-layout__left">
|
||||
<slider />
|
||||
</div>
|
||||
|
||||
<div class="page-layout__right">
|
||||
<div class="app-layout__right">
|
||||
<topbar />
|
||||
<process />
|
||||
<views />
|
||||
|
@ -17,7 +17,7 @@
|
|||
<script lang="ts" setup>
|
||||
import Topbar from "./components/topbar.vue";
|
||||
import Slider from "./components/slider.vue";
|
||||
import Process from "./components/process.vue";
|
||||
import process from "./components/process.vue";
|
||||
import Views from "./components/views.vue";
|
||||
import { useBase } from "/$/base";
|
||||
|
||||
|
@ -25,7 +25,7 @@ const { app } = useBase();
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-layout {
|
||||
.app-layout {
|
||||
display: flex;
|
||||
background-color: #f7f7f7;
|
||||
height: 100%;
|
||||
|
@ -57,7 +57,7 @@ const { app } = useBase();
|
|||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.page-layout__left {
|
||||
.app-layout__left {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
z-index: 9999;
|
||||
|
@ -65,37 +65,37 @@ const { app } = useBase();
|
|||
box-shadow 0.3s cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
}
|
||||
|
||||
.page-layout__right {
|
||||
.app-layout__right {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
.page-layout__left {
|
||||
.app-layout__left {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
.page-layout__mask {
|
||||
.app-layout__mask {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (min-width: 768px) {
|
||||
.page-layout__left,
|
||||
.page-layout__right {
|
||||
.app-layout__left,
|
||||
.app-layout__right {
|
||||
transition: width 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
.page-layout__mask {
|
||||
.app-layout__mask {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.collapse {
|
||||
.page-layout__left {
|
||||
.app-layout__left {
|
||||
width: 64px;
|
||||
}
|
||||
|
||||
.page-layout__right {
|
||||
.app-layout__right {
|
||||
width: calc(100% - 64px);
|
||||
}
|
||||
}
|
|
@ -2,18 +2,6 @@
|
|||
<error-page :code="401" desc="认证失败,请重新登录!" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" name="401" setup>
|
||||
import ErrorPage from "./components/error-page.vue";
|
||||
|
||||
export default {
|
||||
cool: {
|
||||
route: {
|
||||
path: "/401"
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
ErrorPage
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -2,18 +2,6 @@
|
|||
<error-page :code="403" desc="您无权访问此页面" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" name="403" setup>
|
||||
import ErrorPage from "./components/error-page.vue";
|
||||
|
||||
export default {
|
||||
cool: {
|
||||
route: {
|
||||
path: "/403"
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
ErrorPage
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -2,18 +2,6 @@
|
|||
<error-page :code="404" desc="找不到您要查找的页面" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" name="404" setup>
|
||||
import ErrorPage from "./components/error-page.vue";
|
||||
|
||||
export default {
|
||||
cool: {
|
||||
route: {
|
||||
path: "/404"
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
ErrorPage
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -2,18 +2,6 @@
|
|||
<error-page :code="500" desc="糟糕,出了点问题" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" name="500" setup>
|
||||
import ErrorPage from "./components/error-page.vue";
|
||||
|
||||
export default {
|
||||
cool: {
|
||||
route: {
|
||||
path: "/500"
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
ErrorPage
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -2,18 +2,6 @@
|
|||
<error-page :code="502" desc="马上回来" />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
<script lang="ts" name="502" setup>
|
||||
import ErrorPage from "./components/error-page.vue";
|
||||
|
||||
export default {
|
||||
cool: {
|
||||
route: {
|
||||
path: "/502"
|
||||
}
|
||||
},
|
||||
|
||||
components: {
|
||||
ErrorPage
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-button round @click="navTo">跳转</el-button>
|
||||
<el-button type="primary" round @click="navTo">跳转</el-button>
|
||||
</div>
|
||||
|
||||
<ul class="link">
|
||||
|
@ -27,63 +27,45 @@
|
|||
<el-button round @click="toLogin">返回登录页</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<p class="copyright">Copyright © cool-admin-next 2023</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref } from "vue";
|
||||
<script lang="ts" setup>
|
||||
import { ref } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useBase } from "/$/base";
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
code: Number,
|
||||
desc: String
|
||||
},
|
||||
|
||||
setup() {
|
||||
const { router } = useCool();
|
||||
const { user, menu } = useBase();
|
||||
|
||||
const url = ref<string>("");
|
||||
const isLogout = ref<boolean>(false);
|
||||
|
||||
function navTo() {
|
||||
router.push(url.value);
|
||||
}
|
||||
|
||||
function toLogin() {
|
||||
router.push("/login");
|
||||
}
|
||||
|
||||
async function reLogin() {
|
||||
isLogout.value = true;
|
||||
user.logout();
|
||||
}
|
||||
|
||||
function back() {
|
||||
history.back();
|
||||
}
|
||||
|
||||
function home() {
|
||||
router.push("/");
|
||||
}
|
||||
|
||||
return {
|
||||
user,
|
||||
menu,
|
||||
url,
|
||||
isLogout,
|
||||
navTo,
|
||||
toLogin,
|
||||
reLogin,
|
||||
back,
|
||||
home
|
||||
};
|
||||
}
|
||||
defineProps({
|
||||
code: Number,
|
||||
desc: String
|
||||
});
|
||||
|
||||
const { router } = useCool();
|
||||
const { user, menu } = useBase();
|
||||
|
||||
const url = ref<string>("");
|
||||
const isLogout = ref<boolean>(false);
|
||||
|
||||
function navTo() {
|
||||
router.push(url.value);
|
||||
}
|
||||
|
||||
function toLogin() {
|
||||
router.push("/login");
|
||||
}
|
||||
|
||||
async function reLogin() {
|
||||
isLogout.value = true;
|
||||
user.logout();
|
||||
}
|
||||
|
||||
function back() {
|
||||
history.back();
|
||||
}
|
||||
|
||||
function home() {
|
||||
router.push("/");
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -123,13 +105,7 @@ export default defineComponent({
|
|||
|
||||
.el-button {
|
||||
margin-left: 15px;
|
||||
background-color: var(--color-primary);
|
||||
border-color: var(--color-primary);
|
||||
color: #fff;
|
||||
padding: 0 30px;
|
||||
letter-spacing: 1px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,105 +0,0 @@
|
|||
<template>
|
||||
<div class="route-nav">
|
||||
<p v-if="app.browser.isMini" class="title">
|
||||
{{ lastName }}
|
||||
</p>
|
||||
|
||||
<template v-else>
|
||||
<el-breadcrumb>
|
||||
<el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="(item, index) in list" :key="index">{{
|
||||
(item.meta && item.meta.label) || item.name
|
||||
}}</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, ref, watch } from "vue";
|
||||
import _ from "lodash";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useBase } from "/$/base";
|
||||
|
||||
export default defineComponent({
|
||||
name: "route-nav",
|
||||
|
||||
setup() {
|
||||
const { route } = useCool();
|
||||
const { app, menu } = useBase();
|
||||
|
||||
// 数据列表
|
||||
const list = ref<any[]>([]);
|
||||
|
||||
// 监听路由变化
|
||||
watch(
|
||||
() => route,
|
||||
(val: any) => {
|
||||
function deep(item: any) {
|
||||
if (route.path === "/") {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (item.path == route.path) {
|
||||
return item;
|
||||
} else {
|
||||
if (item.children) {
|
||||
const ret = item.children.map(deep).find(Boolean);
|
||||
|
||||
if (ret) {
|
||||
return [item, ret];
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.value = _(menu.group).map(deep).filter(Boolean).flattenDeep().value();
|
||||
|
||||
if (_.isEmpty(list.value)) {
|
||||
list.value.push(val);
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
|
||||
// 最后一个节点名称
|
||||
const lastName = computed(() => _.last(list.value).name);
|
||||
|
||||
return {
|
||||
list,
|
||||
lastName,
|
||||
app
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.route-nav {
|
||||
white-space: nowrap;
|
||||
|
||||
.el-breadcrumb {
|
||||
margin: 0 10px;
|
||||
|
||||
&__inner {
|
||||
font-size: 13px;
|
||||
padding: 0 10px;
|
||||
font-weight: normal;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,259 +0,0 @@
|
|||
<template>
|
||||
<div class="app-slider">
|
||||
<div class="app-slider__logo" @click="toHome">
|
||||
<img :src="Logo" />
|
||||
<span v-if="!app.isFold || app.browser.isMini">{{ app.info.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="app-slider__container">
|
||||
<menu-nav />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="tsx">
|
||||
import { defineComponent, ref, watch, h } from "vue";
|
||||
import { useBase } from "/$/base";
|
||||
import { useCool } from "/@/cool";
|
||||
import Logo from "/@/assets/logo.png";
|
||||
|
||||
export default defineComponent({
|
||||
name: "app-slider",
|
||||
|
||||
components: {
|
||||
MenuNav: {
|
||||
setup() {
|
||||
const { router, route } = useCool();
|
||||
const { menu } = useBase();
|
||||
|
||||
// 是否可见
|
||||
const visible = ref<boolean>(true);
|
||||
|
||||
// 页面跳转
|
||||
function toView(url: string) {
|
||||
if (url != route.path) {
|
||||
router.push(url);
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新菜单
|
||||
function refresh() {
|
||||
visible.value = false;
|
||||
|
||||
setTimeout(() => {
|
||||
visible.value = true;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
// 监听菜单变化
|
||||
watch(menu.list, refresh);
|
||||
|
||||
return {
|
||||
route,
|
||||
visible,
|
||||
toView,
|
||||
refresh,
|
||||
menu
|
||||
};
|
||||
},
|
||||
|
||||
render(ctx: any) {
|
||||
const { app } = useBase();
|
||||
|
||||
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(
|
||||
<el-sub-menu></el-sub-menu>,
|
||||
{
|
||||
index: String(e.id),
|
||||
key: e.id,
|
||||
popperClass: "app-slider__menu"
|
||||
},
|
||||
{
|
||||
title() {
|
||||
return (
|
||||
<div class="wrap">
|
||||
<icon-svg name={e.icon}></icon-svg>
|
||||
<span v-show={!app.isFold || index != 1}>
|
||||
{e.name}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
default() {
|
||||
return deepMenu(e.children, index + 1);
|
||||
}
|
||||
}
|
||||
);
|
||||
} else {
|
||||
html = h(
|
||||
<el-menu-item></el-menu-item>,
|
||||
{
|
||||
index: e.path,
|
||||
key: e.id
|
||||
},
|
||||
{
|
||||
default() {
|
||||
return (
|
||||
<div class="wrap">
|
||||
<icon-svg name={e.icon}></icon-svg>
|
||||
<span v-show={!app.isFold || index != 1}>
|
||||
{e.name}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return html;
|
||||
});
|
||||
}
|
||||
|
||||
const children = deepMenu(ctx.menu.list, 1);
|
||||
|
||||
return (
|
||||
ctx.visible && (
|
||||
<div class="app-slider__menu">
|
||||
<el-menu
|
||||
default-active={ctx.route.path}
|
||||
background-color="transparent"
|
||||
collapse-transition={false}
|
||||
collapse={app.browser.isMini ? false : app.isFold}
|
||||
onSelect={ctx.toView}
|
||||
>
|
||||
{children}
|
||||
</el-menu>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
function toHome() {
|
||||
location.href = "https://cool-js.com";
|
||||
}
|
||||
|
||||
return {
|
||||
toHome,
|
||||
Logo,
|
||||
...useBase()
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.app-slider {
|
||||
height: 100%;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
background-color: #2f3447;
|
||||
|
||||
&__logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 80px;
|
||||
cursor: pointer;
|
||||
|
||||
img {
|
||||
height: 30px;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
span {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
font-size: 26px;
|
||||
margin-left: 10px;
|
||||
font-family: inherit;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
&__container {
|
||||
height: calc(100% - 80px);
|
||||
overflow-y: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__menu {
|
||||
&.el-popper {
|
||||
&.is-light {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border-right: 0;
|
||||
background-color: transparent;
|
||||
|
||||
&--popup {
|
||||
.icon-svg,
|
||||
span {
|
||||
color: #000;
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu__title,
|
||||
&-item {
|
||||
&.is-active,
|
||||
&:hover {
|
||||
background-color: var(--color-primary) !important;
|
||||
|
||||
.icon-svg,
|
||||
span {
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu__title,
|
||||
&-item,
|
||||
&__title {
|
||||
color: #eee;
|
||||
letter-spacing: 0.5px;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
|
||||
.wrap {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.icon-svg {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&--collapse {
|
||||
.wrap {
|
||||
text-align: center;
|
||||
|
||||
.icon-svg {
|
||||
font-size: 18px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -5,58 +5,52 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, onMounted, ref } from "vue";
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
export default defineComponent({
|
||||
emits: ["update:modelValue", "change"],
|
||||
const emit = defineEmits(["update:modelValue", "change"]);
|
||||
|
||||
setup(_, { emit }) {
|
||||
const { service } = useCool();
|
||||
const { service } = useCool();
|
||||
|
||||
// base64
|
||||
const base64 = ref<string>("");
|
||||
// base64
|
||||
const base64 = ref<string>("");
|
||||
|
||||
// svg
|
||||
const svg = ref<string>("");
|
||||
// svg
|
||||
const svg = ref<string>("");
|
||||
|
||||
function refresh() {
|
||||
service.base.open
|
||||
.captcha({
|
||||
height: 40,
|
||||
width: 150
|
||||
})
|
||||
.then(({ captchaId, data }: any) => {
|
||||
if (data.includes(";base64,")) {
|
||||
base64.value = data;
|
||||
} else {
|
||||
svg.value = data;
|
||||
}
|
||||
function refresh() {
|
||||
service.base.open
|
||||
.captcha({
|
||||
height: 40,
|
||||
width: 150
|
||||
})
|
||||
.then(({ captchaId, data }: any) => {
|
||||
if (data.includes(";base64,")) {
|
||||
base64.value = data;
|
||||
} else {
|
||||
svg.value = data;
|
||||
}
|
||||
|
||||
emit("update:modelValue", captchaId);
|
||||
emit("change", {
|
||||
base64,
|
||||
svg,
|
||||
captchaId
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
emit("update:modelValue", captchaId);
|
||||
emit("change", {
|
||||
base64,
|
||||
svg,
|
||||
captchaId
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
base64,
|
||||
svg,
|
||||
refresh
|
||||
};
|
||||
}
|
||||
onMounted(() => {
|
||||
refresh();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
refresh
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div class="page-login">
|
||||
<div class="box">
|
||||
<img class="logo" :src="Logo" alt="Logo" />
|
||||
<div class="logo">
|
||||
<img src="/logo.png" alt="Logo" />
|
||||
<span>{{ app.info.name }}</span>
|
||||
</div>
|
||||
<p class="desc">一款快速开发后台权限管理系统</p>
|
||||
|
||||
<el-form label-position="top" class="form" :disabled="saving" size="large">
|
||||
|
@ -54,96 +57,73 @@
|
|||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, reactive, ref } from "vue";
|
||||
<script lang="ts" name="login" setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useBase } from "/$/base";
|
||||
import Captcha from "./components/captcha.vue";
|
||||
import Logo from "/@/assets/logo-text.png";
|
||||
|
||||
export default defineComponent({
|
||||
cool: {
|
||||
route: {
|
||||
path: "/login"
|
||||
}
|
||||
},
|
||||
const { refs, setRefs, router, service } = useCool();
|
||||
const { user, menu, app } = useBase();
|
||||
|
||||
components: {
|
||||
Captcha
|
||||
},
|
||||
// 状态1
|
||||
const saving = ref(false);
|
||||
|
||||
setup() {
|
||||
const { refs, setRefs, router, service } = useCool();
|
||||
const { user, menu } = useBase();
|
||||
// 表单数据
|
||||
const form = reactive({
|
||||
username: "",
|
||||
password: "",
|
||||
captchaId: "",
|
||||
verifyCode: ""
|
||||
});
|
||||
|
||||
// 状态1
|
||||
const saving = ref(false);
|
||||
// 登录
|
||||
async function toLogin() {
|
||||
if (!form.username) {
|
||||
return ElMessage.error("用户名不能为空");
|
||||
}
|
||||
|
||||
// 表单数据
|
||||
const form = reactive({
|
||||
username: "",
|
||||
password: "",
|
||||
captchaId: "",
|
||||
verifyCode: ""
|
||||
if (!form.password) {
|
||||
return ElMessage.error("密码不能为空");
|
||||
}
|
||||
|
||||
if (!form.verifyCode) {
|
||||
return ElMessage.error("图片验证码不能为空");
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
|
||||
try {
|
||||
// 登录
|
||||
await service.base.open.login(form).then((res) => {
|
||||
user.setToken(res);
|
||||
});
|
||||
|
||||
// 登录
|
||||
async function toLogin() {
|
||||
if (!form.username) {
|
||||
return ElMessage.error("用户名不能为空");
|
||||
}
|
||||
// 用户信息
|
||||
user.get();
|
||||
|
||||
if (!form.password) {
|
||||
return ElMessage.error("密码不能为空");
|
||||
}
|
||||
// 权限菜单
|
||||
await menu.get();
|
||||
|
||||
if (!form.verifyCode) {
|
||||
return ElMessage.error("图片验证码不能为空");
|
||||
}
|
||||
|
||||
saving.value = true;
|
||||
|
||||
try {
|
||||
// 登录
|
||||
await service.base.open.login(form).then((res) => {
|
||||
user.setToken(res);
|
||||
});
|
||||
|
||||
// 用户信息
|
||||
await user.get();
|
||||
|
||||
// 权限菜单
|
||||
await menu.get();
|
||||
|
||||
// 跳转地址
|
||||
menu.getPath();
|
||||
|
||||
router.push("/");
|
||||
} catch (err: any) {
|
||||
refs.value.captcha.refresh();
|
||||
ElMessage.error(err.message);
|
||||
}
|
||||
|
||||
saving.value = false;
|
||||
}
|
||||
|
||||
return {
|
||||
refs,
|
||||
setRefs,
|
||||
form,
|
||||
saving,
|
||||
toLogin,
|
||||
Logo
|
||||
};
|
||||
// 跳转
|
||||
router.push("/");
|
||||
} catch (err: any) {
|
||||
refs.value.captcha.refresh();
|
||||
ElMessage.error(err.message);
|
||||
}
|
||||
});
|
||||
|
||||
saving.value = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.page-login {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background-color: #2f3447;
|
||||
|
||||
|
@ -152,15 +132,24 @@ export default defineComponent({
|
|||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 500px;
|
||||
width: 500px;
|
||||
position: absolute;
|
||||
left: calc(50% - 250px);
|
||||
top: calc(50% - 250px);
|
||||
|
||||
.logo {
|
||||
height: 50px;
|
||||
margin-bottom: 30px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #fff;
|
||||
|
||||
img {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 38px;
|
||||
margin-left: 10px;
|
||||
letter-spacing: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
|
||||
.desc {
|
||||
|
|
|
@ -1 +1,46 @@
|
|||
* {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei",
|
||||
"微软雅黑", Arial, sans-serif;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background-color: rgba(144, 147, 153, 0.3);
|
||||
}
|
||||
|
||||
*::-webkit-scrollbar-track {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
#app {
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
:root {
|
||||
--view-bg-color: #f7f7f7;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
input,
|
||||
button {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
input {
|
||||
&:-webkit-autofill {
|
||||
box-shadow: 0 0 0px 1000px white inset;
|
||||
}
|
||||
}
|
||||
|
||||
@import "./theme.scss";
|
||||
|
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 853 B After Width: | Height: | Size: 853 B |
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
Before Width: | Height: | Size: 859 B After Width: | Height: | Size: 859 B |
Before Width: | Height: | Size: 763 B After Width: | Height: | Size: 763 B |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 675 B After Width: | Height: | Size: 675 B |
Before Width: | Height: | Size: 917 B After Width: | Height: | Size: 917 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 753 B After Width: | Height: | Size: 753 B |
Before Width: | Height: | Size: 965 B After Width: | Height: | Size: 965 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
Before Width: | Height: | Size: 747 B After Width: | Height: | Size: 747 B |
Before Width: | Height: | Size: 797 B After Width: | Height: | Size: 797 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 740 B After Width: | Height: | Size: 740 B |