This commit is contained in:
icssoa 2022-04-06 02:36:20 +08:00
parent 19d5e741ea
commit e4f7f54b71
25 changed files with 1194 additions and 50426 deletions

View File

@ -9,9 +9,8 @@
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix" "lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix"
}, },
"dependencies": { "dependencies": {
"@cool-vue/crud": "^1.0.6", "@cool-vue/crud": "^5.0.0",
"@element-plus/icons-vue": "^1.1.3", "@element-plus/icons-vue": "^1.1.3",
"@types/quill": "^2.0.9",
"axios": "^0.26.1", "axios": "^0.26.1",
"clipboard": "^2.0.10", "clipboard": "^2.0.10",
"codemirror": "^5.62.0", "codemirror": "^5.62.0",
@ -38,6 +37,7 @@
"@types/lodash": "^4.14.168", "@types/lodash": "^4.14.168",
"@types/node": "^16.10.2", "@types/node": "^16.10.2",
"@types/nprogress": "^0.2.0", "@types/nprogress": "^0.2.0",
"@types/quill": "^2.0.9",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^4.20.0", "@typescript-eslint/eslint-plugin": "^4.20.0",
"@typescript-eslint/parser": "^4.20.0", "@typescript-eslint/parser": "^4.20.0",

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
<template> <template>
<el-config-provider :locale="locale"> <el-config-provider :locale="zhCn">
<div class="preload" v-if="app.loading"> <div class="preload" v-if="app.loading">
<div class="container"> <div class="container">
<p class="name">{{ app.info.name }}</p> <p class="name">{{ app.info.name }}</p>
@ -17,26 +17,12 @@
</el-config-provider> </el-config-provider>
</template> </template>
<script lang="ts"> <script lang="ts" setup>
import { defineComponent } from "vue";
import { ElConfigProvider } from "element-plus"; import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn"; import zhCn from "element-plus/lib/locale/lang/zh-cn";
import { useBaseStore } from "/$/base"; import { useBaseStore } from "/$/base";
export default defineComponent({ const { app } = useBaseStore();
components: {
[ElConfigProvider.name]: ElConfigProvider
},
setup() {
const locale = zhCn;
return {
locale,
...useBaseStore()
};
}
});
</script> </script>
<style lang="scss" src="./assets/css/index.scss"></style> <style lang="scss" src="./assets/css/index.scss"></style>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.7 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1,37 +1,42 @@
import { getUrlParam } from "./utils"; import { getUrlParam, storage } from "./utils";
import storage from "./utils/storage";
// 路由模式
export const routerMode: String = "history";
// 开发模式 // 开发模式
export const isDev: Boolean = import.meta.env.MODE === "development"; const isDev: Boolean = import.meta.env.MODE === "development";
// Host // Host
export const host: String = "https://show.cool-admin.com"; const host: String = "https://show.cool-admin.com";
// 请求地址 // 请求地址
export const baseUrl: String = (function () { const baseUrl: String = (function () {
let proxy = getUrlParam("proxy"); let proxy = getUrlParam("proxy");
if (proxy) { if (proxy) {
storage.set("proxy", proxy); storage.set("proxy", proxy);
} else { } else {
proxy = storage.get("proxy") || "dev"; proxy = storage.get("proxy") || "pro";
} }
return isDev ? `/${proxy}` : `/api`; return isDev ? `/${proxy}` : `/api`;
})(); })();
// 字体图标库 // 应用配置
export const iconfont = []; const app = {
// 程序配置参数
export const app: any = {
name: "COOL-ADMIN", name: "COOL-ADMIN",
// 自定义菜单列表 // 菜单
menuList: [], menu: {
list: []
},
// 路由
router: {
// 模式
mode: "history",
// 页面
pages: [],
// 视图 / 路由下的 children
views: []
},
// 主题 // 主题
theme: { theme: {
@ -39,11 +44,14 @@ export const app: any = {
color: "", color: "",
// 样式地址 // 样式地址
url: "" url: ""
} },
// 字体图标库
iconfont: []
}; };
// 忽略规则 // 忽略规则
export const ignore = { const ignore = {
// 不显示请求进度条 // 不显示请求进度条
NProgress: ["/sys/info/record"], NProgress: ["/sys/info/record"],
// 页面不需要登录验证 // 页面不需要登录验证
@ -51,8 +59,10 @@ export const ignore = {
}; };
// 测试 // 测试
export const test = { const test = {
token: "", token: "",
mock: false, mock: false,
eps: true eps: true
}; };
export { isDev, host, baseUrl, app, ignore, test };

View File

@ -8,12 +8,9 @@ import {
} from "vue-router"; } from "vue-router";
import { storage } from "/@/cool"; import { storage } from "/@/cool";
import { useBaseStore } from "/$/base"; import { useBaseStore } from "/$/base";
import { routerMode } from "/@/cool/config"; import { app, ignore } from "/@/cool/config";
// 忽略 const { views, pages, mode } = app.router;
const ignore: any = {
token: ["/login", "/403", "/404", "/500", "/502"]
};
// 默认路由 // 默认路由
const routes: RouteRecordRaw[] = [ const routes: RouteRecordRaw[] = [
@ -26,9 +23,11 @@ const routes: RouteRecordRaw[] = [
path: "/", path: "/",
name: "数据统计", name: "数据统计",
component: () => import("/@/views/home/index.vue") component: () => import("/@/views/home/index.vue")
} },
...views
] ]
}, },
...pages,
{ {
path: "/:catchAll(.*)", path: "/:catchAll(.*)",
name: "404", name: "404",
@ -38,7 +37,7 @@ const routes: RouteRecordRaw[] = [
// 创建 // 创建
const router = createRouter({ const router = createRouter({
history: routerMode == "history" ? createWebHistory() : createWebHashHistory(), history: mode == "history" ? createWebHistory() : createWebHashHistory(),
routes routes
}); });

View File

@ -1,4 +1,4 @@
import { routerMode } from "../config"; import { app } from "../config";
import storage from "./storage"; import storage from "./storage";
import module from "./module"; import module from "./module";
@ -317,7 +317,7 @@ export function href(path: string, newWindow?: boolean) {
let url = ""; let url = "";
if (routerMode == "history") { if (app.router.mode == "history") {
url = origin + import.meta.env.BASE_URL + path.substr(1); url = origin + import.meta.env.BASE_URL + path.substr(1);
} else { } else {
url = origin + import.meta.env.BASE_URL + "#" + path; url = origin + import.meta.env.BASE_URL + "#" + path;

2
src/env.d.ts vendored
View File

@ -1,2 +1,2 @@
/// <reference types="@cool-vue/crud/index" /> /// <reference types="@cool-vue/crud" />
/// <reference types="../build/cool/temp/service" /> /// <reference types="../build/cool/temp/service" />

View File

@ -1,343 +0,0 @@
<template>
<div class="cl-upload__wrap">
<div
class="cl-upload"
:class="{
'is-picture': true
}"
>
<draggable v-model="list" v-bind="drag.options" item-key="index">
<template #item="{ element: item, index }">
<el-upload
class="is-drag"
action=""
:accept="accept"
:show-file-list="false"
:http-request="
(req) => {
httpRequest(req, item);
}
"
:before-upload="
(file) => {
beforeUpload(file, item);
}
"
:headers="{
Authorization: user.token
}"
>
<div class="cl-upload__item">
<template v-if="item._url || item.url">
<!-- 图片 -->
<cl-image
class="cl-upload__image"
:src="item._url || item.url"
fit="cover"
></cl-image>
<!-- 工具 -->
<div class="cl-upload__actions">
<span @click.stop="onPreview(item)">
<el-icon><zoom-in /></el-icon>
</span>
<span @click.stop="onRemove(index)">
<el-icon><delete /></el-icon>
</span>
</div>
</template>
<template v-else>
<el-icon :size="24"><picture-filled /></el-icon>
<span class="cl-upload__text">图片上传</span>
</template>
<!-- 进度条 -->
<div
class="cl-upload__process"
v-if="item.process > 0 && item.process < 100"
>
<el-progress
:percentage="item.process"
:show-text="false"
></el-progress>
</div>
</div>
</el-upload>
</template>
</draggable>
<el-upload
class="is-drag"
action=""
:accept="accept"
:show-file-list="false"
:http-request="httpRequest"
:before-upload="beforeUpload"
:headers="{
Authorization: user.token
}"
>
<div class="cl-upload__item">
<el-icon :size="24"><picture-filled /></el-icon>
<span class="cl-upload__text">图片上传</span>
</div>
</el-upload>
</div>
</div>
<cl-dialog v-model="preview.visible" title="图片预览" width="500px">
<img style="width: 100%" :src="preview.url" />
</cl-dialog>
</template>
<script lang="ts">
export default {
name: "cl-upload"
};
</script>
<script lang="ts" setup>
import { PictureFilled, ZoomIn, Delete } from "@element-plus/icons-vue";
import { computed, ref, reactive } from "vue";
import { v4 as uuidv4 } from "uuid";
import { useCool } from "/@/cool";
import { last } from "/@/cool/utils";
import { ElMessage, UploadRawFile } from "element-plus";
import { useBaseStore } from "/$/base";
import Draggable from "vuedraggable/src/vuedraggable";
const props = defineProps({
rename: {
type: Boolean,
default: true
}
});
const { service } = useCool();
//
const { user } = useBaseStore();
//
const preview = reactive<any>({
visible: false,
url: ""
});
//
const list = ref<any[]>([
{
url: ""
}
]);
//
const drag = reactive<any>({
options: {
group: "Upload",
animation: 300,
ghostClass: "Ghost",
dragClass: "Drag",
draggable: ".is-drag"
}
});
//
const accept = computed(() => {
return "image/*";
});
//
function beforeUpload(file: UploadRawFile, item: any) {
console.log(item);
if (!item.uid) {
list.value.push({
url: ""
});
}
Object.assign(item, {
type: file.type,
uid: file.uid,
name: file.name,
url: "",
_url: window.webkitURL.createObjectURL(file),
process: 0
});
}
//
function onRemove(index: number) {
list.value.splice(index, 1);
}
//
function onPreview(item: any) {
preview.visible = true;
preview.url = item.url;
}
//
async function httpRequest(req: any, item: any) {
console.log(item, list.value);
try {
const { mode, type } = await service.base.comm.uploadMode();
//
function upload(file: UploadRawFile) {
return new Promise((resolve, reject) => {
function next(res: any) {
const data = new FormData();
for (const i in res) {
if (i != "host") {
data.append(i, res[i]);
}
}
let fileName = file.name;
// uuid
if (props.rename) {
fileName = uuidv4() + "." + last((file.name || "").split("."));
}
data.append("key", `app/${fileName}`);
data.append("file", file);
//
service
.request({
url: res.host,
method: "POST",
headers: {
"Content-Type": "multipart/form-data"
},
data,
onUploadProgress(e: any) {
item.process = parseInt((e.loaded / e.total) * 100);
}
})
.then((url) => {
if (mode === "local") {
resolve(url);
} else {
resolve(`${res.host}/app/${fileName}`);
}
})
.catch(reject);
}
if (mode == "local") {
next({
host: "/admin/base/comm/upload"
});
} else {
service.base.comm.upload().then(next).catch(reject);
}
});
}
await upload(req.file)
.then((url) => {
item.url = url;
})
.catch((err) => {
ElMessage.error(err.message);
});
} catch (e) {
ElMessage.error("上传配置错误");
}
}
</script>
<style lang="scss">
.cl-upload {
& > div {
display: flex;
flex-wrap: wrap;
margin: 0 -5px;
}
.Ghost {
opacity: 0.7;
}
.Drag {
border: 1px dashed #000 !important;
background: #fff !important;
}
&.is-picture {
.cl-upload {
&__item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
height: 100px;
width: 100px;
margin: 5px;
color: #333;
box-sizing: border-box;
&:hover {
border-color: currentColor;
color: $color-primary;
.cl-upload__actions {
display: inline-flex;
}
}
}
&__image {
height: 100%;
width: 100%;
border-radius: 6px;
overflow: hidden;
}
&__text {
font-size: 12px;
line-height: normal;
margin-top: 5px;
}
&__actions {
position: absolute;
display: none;
align-items: center;
justify-content: center;
z-index: 9;
height: 100%;
width: 100%;
background-color: rgba(0, 0, 0, 0.5);
span {
display: inline-flex;
align-items: center;
justify-content: center;
height: 30px;
width: 30px;
color: #fff;
font-size: 18px;
}
}
&__process {
position: absolute;
bottom: 10px;
left: 10px;
width: calc(100% - 20px);
}
}
}
}
</style>

View File

@ -1,4 +1,4 @@
import { iconfont, app } from "/@/cool/config"; import { app } from "/@/cool/config";
import { basename } from "/@/cool/utils"; import { basename } from "/@/cool/utils";
import { createLink } from "../utils"; import { createLink } from "../utils";
@ -14,8 +14,8 @@ if (app.theme) {
} }
// 字体图标库加载 // 字体图标库加载
if (iconfont) { if (app.iconfont) {
iconfont.forEach((e: string) => { app.iconfont.forEach((e: string) => {
createLink(e); createLink(e);
}); });
} }

View File

@ -7,7 +7,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, computed } from "vue"; import { defineComponent, computed } from "vue";
import { isArray, isString, last } from "/@/cool/utils"; import { isArray, isString, last } from "/@/cool/utils";
import { Link } from "@element-plus/icons"; import { Link } from "@element-plus/icons-vue";
export default defineComponent({ export default defineComponent({
name: "cl-link", name: "cl-link",

View File

@ -16,12 +16,7 @@
<div class="flex1"></div> <div class="flex1"></div>
<!-- 工具栏 --> <!-- 工具栏 -->
<ul class="app-topbar__tools"> <ul class="app-topbar__tools"></ul>
<!-- 主题 -->
<!-- <li v-if="modules.theme">
<cl-theme />
</li> -->
</ul>
<!-- 用户信息 --> <!-- 用户信息 -->
<div class="app-topbar__user" v-if="user.info"> <div class="app-topbar__user" v-if="user.info">

View File

@ -57,7 +57,6 @@ const { service } = useCool();
const Tree = ref<any>(); const Tree = ref<any>();
const Form = useForm(() => { const Form = useForm(() => {
console.log(111);
refresh(); refresh();
}); });

View File

@ -64,7 +64,6 @@ export default defineComponent({
}) })
.then(() => { .then(() => {
ElMessage.success("转移成功"); ElMessage.success("转移成功");
console.log(Crud);
Crud.value?.refresh(); Crud.value?.refresh();
close(); close();
}) })

View File

@ -1,354 +0,0 @@
<template>
<div class="cl-theme">
<li @click="open">
<icon-svg :size="18" name="icon-theme" />
</li>
<!-- 系统设置 -->
<el-drawer v-model="drawer.visible" title="系统设置" size="300px">
<div class="cl-theme__container">
<div class="cl-theme__color is-card">
<p>主题</p>
<ul>
<el-tooltip
v-for="(item, name) in themeList"
:key="name"
:content="item.label"
placement="top"
>
<li
:style="{
backgroundColor: item.color
}"
@click="setTheme(item)"
>
<i
v-show="item.color == form.theme.color"
class="el-icon-check"
></i>
</li>
</el-tooltip>
</ul>
</div>
<div class="cl-theme__switch is-card">
<p>内容区域</p>
<ul>
<li v-if="!browser.isMini">
<span>显示一级菜单栏</span>
<el-switch v-model="form.conf.showAMenu" @change="onAMenuChange" />
</li>
<li>
<span>显示路由导航栏</span>
<el-switch v-model="form.conf.showRouteNav" />
</li>
<li>
<span>显示页面进程栏</span>
<el-switch v-model="form.conf.showProcess" />
</li>
</ul>
</div>
<div class="cl-theme__append">
<slot></slot>
</div>
<div class="cl-theme__tips" v-if="isDev">
<el-alert
type="warning"
:closable="false"
show-icon
title="手动修改配置文件可设置为默认主题、布局。"
/>
<el-button
round
type="primary"
size="small"
style="width: 100%"
:disabled="!form.theme.url"
@click="openDesc"
>修改说明</el-button
>
</div>
</div>
</el-drawer>
<!-- 修改说明 -->
<cl-dialog
v-model="desc.visible"
title="修改说明"
width="800px"
:props="{
'append-to-body': true
}"
>
<ul class="cl-theme__desc">
<li>
<p class="cl-theme__desc-label">修改主题色</p>
<cl-codemirror v-model="desc.color" />
</li>
<li>
<p class="cl-theme__desc-label">修改应用配置</p>
<cl-codemirror v-model="desc.conf" />
</li>
</ul>
</cl-dialog>
</div>
</template>
<script>
import { isDev } from "/@/cool/config";
import { isArray, cloneDeep } from "/@/cool/utils";
export default {
name: "cl-theme",
props: {
list: Array
},
data() {
return {
drawer: {
visible: false
},
desc: {
visible: false,
color: "",
conf: ""
},
themes: [
{
label: "钴蓝",
name: "blue",
color: "#4165d7"
},
{
label: "极黑",
name: "black",
color: "#2f3447"
},
{
label: "果绿",
name: "green",
color: "#51C21A"
},
{
label: "酱紫",
name: "purple",
color: "#d0378d"
}
],
isDev,
form: {
name: "",
conf: {
showAMenu: false,
showRouteNav: true,
showProcess: true
},
theme: {
color: "",
url: ""
}
}
};
},
computed: {
themeList() {
return isArray(this.list) ? this.list : this.themes;
}
},
watch: {
app: {
immediate: true,
handler(val) {
this.form = cloneDeep(val);
}
},
form: {
deep: true,
handler(val) {
this.$store.commit("UPDATE_APP", val);
}
}
},
methods: {
open() {
this.drawer.visible = true;
},
close() {
this.drawer.visible = false;
},
onAMenuChange() {
setTimeout(() => {
this.$store.commit("SET_MENU_LIST");
}, 0);
},
//
setTheme({ name, color, label }) {
if (this.form.theme.color == color) {
return false;
}
this.$message.success(`切换主题:${label}`);
const theme = document.getElementById("theme-style");
const style = theme || document.createElement("link");
style.href = `${this.modules.theme.options.sourceUrl || "/theme/"}${name}.css`;
if (!theme) {
style.type = "text/css";
style.rel = "stylesheet";
style.id = "theme-style";
document.getElementsByTagName("head").item(0).appendChild(style);
}
//
this.form.theme.color = color;
this.form.theme.url = style.href;
// css
document.getElementsByTagName("body")[0].style.setProperty("--color-primary", color);
},
//
openDesc() {
this.desc.visible = true;
this.desc.color = `
// src/assets/css/common.scss
$primary: ${this.form.theme.color};
`;
this.desc.conf = `
// src/cool/config.js
export const app = {
conf: ${JSON.stringify(this.form.conf)},
theme: {
url: "${this.form.theme.url}"
}
}
`;
}
}
};
</script>
<style lang="scss">
.cl-theme {
.el-drawer {
&__header {
margin-bottom: 20px;
}
&__body {
height: calc(100% - 63px);
padding: 0 5px;
}
}
&__container {
height: 100%;
overflow: auto;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-thumb {
border-radius: 6px;
background-color: rgba(144, 147, 153, 0.3);
}
}
.is-card {
padding: 20px 0;
margin: 0 20px 20px 20px;
border-bottom: 1px solid #f7f7f7;
& > p {
font-size: 15px;
font-weight: bold;
margin-bottom: 10px;
}
}
&__switch {
ul {
width: 100%;
li {
display: flex;
justify-content: space-between;
align-items: center;
height: 40px;
list-style: none;
span {
font-size: 13px;
}
}
}
}
&__color {
ul {
display: flex;
flex-wrap: wrap;
margin-top: 10px;
li {
list-style: none;
height: 20px;
width: 20px;
border-radius: 3px;
margin-right: 10px;
margin-top: 10px;
text-align: center;
color: #fff;
line-height: 20px;
&:hover {
opacity: 0.7;
}
}
}
}
&__tips {
padding: 10px 20px;
margin-bottom: 50px;
.el-button {
margin-top: 20px;
}
}
&__desc {
padding: 10px;
&-label {
margin-bottom: 10px;
}
li {
list-style: none;
margin-bottom: 20px;
}
}
}
</style>

View File

@ -209,7 +209,6 @@ function close() {
// //
function onSuccess(data: any) { function onSuccess(data: any) {
console.log(data.classifyId);
service.space.info service.space.info
.add(data) .add(data)
.then((res) => { .then((res) => {

View File

@ -81,8 +81,6 @@ const Form = useForm();
// //
function edit(item: any = {}) { function edit(item: any = {}) {
console.log(item);
Form.value?.open({ Form.value?.open({
title: "添加分类", title: "添加分类",
width: "400px", width: "400px",

View File

@ -64,6 +64,7 @@ import HotSearch from "./components/hot-search.vue";
<style lang="scss"> <style lang="scss">
.view-home { .view-home {
padding-right: 10px;
.card { .card {
background-color: #fff; background-color: #fff;
border-radius: 5px; border-radius: 5px;

View File

@ -22,12 +22,6 @@ export default (): UserConfig => {
rewrite: (path: string) => path.replace(/^\/dev/, "") rewrite: (path: string) => path.replace(/^\/dev/, "")
}, },
"/ap": {
target: "http://dev.frp.cool-js.cloud",
changeOrigin: true,
rewrite: (path: string) => path.replace(/^\/ap/, "")
},
"/pro": { "/pro": {
target: "https://show.cool-admin.com", target: "https://show.cool-admin.com",
changeOrigin: true, changeOrigin: true,
@ -60,7 +54,7 @@ export default (): UserConfig => {
} }
}, },
server: { server: {
port: 9100, port: 9000,
proxy, proxy,
hmr: { hmr: {
overlay: true overlay: true
@ -68,7 +62,7 @@ export default (): UserConfig => {
}, },
define: { define: {
__PROXY_LIST__: JSON.stringify(proxy), __PROXY_LIST__: JSON.stringify(proxy),
__SERVER_PORT__: 9100 __SERVER_PORT__: 9000
}, },
build: { build: {
sourcemap: false, sourcemap: false,

2706
yarn.lock

File diff suppressed because it is too large Load Diff