This commit is contained in:
parent
7783fb5962
commit
95bcfd5990
|
@ -5,7 +5,7 @@
|
|||
"body": [
|
||||
"<template>",
|
||||
" <cl-crud ref=\"Crud\">",
|
||||
" <el-row>",
|
||||
" <cl-row>",
|
||||
" <!-- 刷新按钮 -->",
|
||||
" <cl-refresh-btn />",
|
||||
" <!-- 新增按钮 -->",
|
||||
|
@ -15,18 +15,18 @@
|
|||
" <cl-flex1 />",
|
||||
" <!-- 关键字搜索 -->",
|
||||
" <cl-search-key />",
|
||||
" </el-row>",
|
||||
" </cl-row>",
|
||||
"",
|
||||
" <el-row>",
|
||||
" <cl-row>",
|
||||
" <!-- 数据表格 -->",
|
||||
" <cl-table ref=\"Table\" />",
|
||||
" </el-row>",
|
||||
" </cl-row>",
|
||||
"",
|
||||
" <el-row>",
|
||||
" <cl-row>",
|
||||
" <cl-flex1 />",
|
||||
" <!-- 分页控件 -->",
|
||||
" <cl-pagination />",
|
||||
" </el-row>",
|
||||
" </cl-row>",
|
||||
"",
|
||||
" <!-- 新增、编辑 -->",
|
||||
" <cl-upsert ref=\"Upsert\" />",
|
||||
|
|
|
@ -232,9 +232,7 @@ export async function createMenu({ router, columns, prefix, api, filePath }: any
|
|||
upsert.items.push(format(item));
|
||||
}
|
||||
|
||||
if (
|
||||
!["cl-codemirror", "cl-editor-quill", "cl-editor-wang"].includes(column.component?.name)
|
||||
) {
|
||||
if (!column.component?.name.includes("cl-editor-")) {
|
||||
table.columns.push(format(column));
|
||||
}
|
||||
});
|
||||
|
@ -288,7 +286,7 @@ export async function createMenu({ router, columns, prefix, api, filePath }: any
|
|||
// 代码模板
|
||||
const temp = `<template>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<!-- 刷新按钮 -->
|
||||
<cl-refresh-btn />
|
||||
${permission.add ? "<!-- 新增按钮 -->\n<cl-add-btn />" : ""}
|
||||
|
@ -296,18 +294,18 @@ export async function createMenu({ router, columns, prefix, api, filePath }: any
|
|||
<cl-flex1 />
|
||||
<!-- 关键字搜索 -->
|
||||
<cl-search-key />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<!-- 数据表格 -->
|
||||
<cl-table ref="Table" />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<!-- 分页控件 -->
|
||||
<cl-pagination />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<!-- 新增、编辑 -->
|
||||
<cl-upsert ref="Upsert" />
|
||||
|
|
|
@ -178,19 +178,13 @@ export default [
|
|||
{
|
||||
test: ["rich", "text", "html", "content", "introduce", "description", "desc"],
|
||||
form: {
|
||||
name: "cl-editor-wang",
|
||||
props: {
|
||||
height: 400
|
||||
}
|
||||
name: "cl-editor-wang"
|
||||
}
|
||||
},
|
||||
{
|
||||
test: ["code", "codes"],
|
||||
form: {
|
||||
name: "cl-codemirror",
|
||||
props: {
|
||||
height: 400
|
||||
}
|
||||
name: "cl-editor-monaco"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
12
package.json
12
package.json
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "front-next",
|
||||
"version": "5.12.1",
|
||||
"version": "5.12.2",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
"build": "vite build",
|
||||
|
@ -9,15 +9,12 @@
|
|||
"lint:eslint": "eslint \"{src,mock}/**/*.{vue,ts,tsx}\" --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/lang-javascript": "^6.1.2",
|
||||
"@codemirror/theme-one-dark": "^6.1.0",
|
||||
"@cool-vue/crud": "^5.9.4",
|
||||
"@cool-vue/crud": "^6.0.1",
|
||||
"@element-plus/icons-vue": "^2.0.10",
|
||||
"@vueuse/core": "^9.1.0",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^0.27.2",
|
||||
"codemirror": "^6.0.1",
|
||||
"core-js": "^3.23.5",
|
||||
"echarts": "^5.3.3",
|
||||
"element-plus": "^2.2.28",
|
||||
|
@ -25,13 +22,13 @@
|
|||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"monaco-editor": "^0.34.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"pinia": "^2.0.28",
|
||||
"quill": "^1.3.7",
|
||||
"socket.io-client": "^4.5.1",
|
||||
"store": "^2.0.12",
|
||||
"vue": "^3.2.45",
|
||||
"vue-codemirror": "^6.1.1",
|
||||
"vue-echarts": "^6.2.3",
|
||||
"vue-router": "^4.1.6",
|
||||
"vuedraggable": "^4.1.0",
|
||||
|
@ -42,7 +39,7 @@
|
|||
"@types/mockjs": "^1.0.6",
|
||||
"@types/node": "^18.0.6",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/quill": "^2.0.9",
|
||||
"@types/quill": "^2.0.10",
|
||||
"@types/store": "^2.0.2",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@typescript-eslint/eslint-plugin": "^5.30.6",
|
||||
|
@ -60,6 +57,7 @@
|
|||
"lodash": "^4.17.21",
|
||||
"magic-string": "^0.26.2",
|
||||
"prettier": "^2.7.1",
|
||||
"rollup-plugin-visualizer": "^5.9.0",
|
||||
"sass": "^1.53.0",
|
||||
"sass-loader": "^13.0.2",
|
||||
"typescript": "^4.7.4",
|
||||
|
|
|
@ -35,7 +35,7 @@ export const config = {
|
|||
// 忽略规则
|
||||
ignore: {
|
||||
// 不显示请求进度条
|
||||
NProgress: ["/sys/info/record"],
|
||||
NProgress: ["/base/comm/upload", "/base/comm/uploadMode"],
|
||||
// 页面不需要登录验证
|
||||
token: ["/login", "/401", "/403", "/404", "/500", "/502"]
|
||||
},
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
import { useEventListener } from "@vueuse/core";
|
||||
import { reactive, watch } from "vue";
|
||||
import { getBrowser } from "../utils";
|
||||
|
||||
const browser = reactive(getBrowser());
|
||||
const events: (() => void)[] = [];
|
||||
|
||||
watch(
|
||||
() => browser.screen,
|
||||
() => {
|
||||
events.forEach((ev) => ev());
|
||||
}
|
||||
);
|
||||
|
||||
useEventListener(window, "resize", () => {
|
||||
Object.assign(browser, getBrowser());
|
||||
});
|
||||
|
||||
export function useBrowser() {
|
||||
return {
|
||||
browser,
|
||||
onScreenChange(ev: () => void, immediate = true) {
|
||||
events.push(ev);
|
||||
|
||||
if (immediate) {
|
||||
ev();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
|
@ -1,36 +1,45 @@
|
|||
import { Emitter } from "mitt";
|
||||
import { onBeforeUpdate, ref, inject } from "vue";
|
||||
import { isNumber } from "lodash-es";
|
||||
import { inject, getCurrentInstance, Ref, reactive } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { service } from "../service";
|
||||
import { Data } from "../utils";
|
||||
import { useBrowser } from "./browser";
|
||||
|
||||
export function useService(): Eps.Service {
|
||||
return Data.get("service" || service);
|
||||
}
|
||||
|
||||
export function useRefs() {
|
||||
const refs: any = ref<any[]>([]);
|
||||
|
||||
onBeforeUpdate(() => {
|
||||
refs.value = [];
|
||||
});
|
||||
|
||||
const setRefs = (index: string) => (el: any) => {
|
||||
refs.value[index] = el;
|
||||
};
|
||||
const refs = reactive<{ [key: string]: any }>({});
|
||||
function setRefs(name: string) {
|
||||
return (el: any) => {
|
||||
refs[name] = el;
|
||||
};
|
||||
}
|
||||
|
||||
return { refs, setRefs };
|
||||
}
|
||||
|
||||
export function useComm() {
|
||||
function px(val: string | number) {
|
||||
return isNumber(val) ? `${val}px` : val;
|
||||
export function useParent(name: string, r: Ref) {
|
||||
const d = getCurrentInstance();
|
||||
|
||||
if (d) {
|
||||
let parent = d.proxy?.$.parent;
|
||||
|
||||
if (parent) {
|
||||
while (parent && parent.type?.name != name && parent.type?.name != "cl-crud") {
|
||||
parent = parent?.parent;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
if (parent.type.name == name) {
|
||||
r.value = parent.proxy;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
px
|
||||
};
|
||||
return r;
|
||||
}
|
||||
|
||||
export function useCool() {
|
||||
|
@ -39,6 +48,9 @@ export function useCool() {
|
|||
route: useRoute(),
|
||||
router: useRouter(),
|
||||
mitt: inject("mitt") as Emitter<any>,
|
||||
...useBrowser(),
|
||||
...useRefs()
|
||||
};
|
||||
}
|
||||
|
||||
export * from "./browser";
|
||||
|
|
|
@ -21,6 +21,10 @@ const routes: RouteRecordRaw[] = [
|
|||
component: config.app.router.home
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/:pathMatch(.*)*",
|
||||
component: () => import("/$/base/pages/error/404.vue")
|
||||
}
|
||||
];
|
||||
|
||||
|
@ -81,7 +85,7 @@ router.clear = function () {
|
|||
const rs = router.getRoutes();
|
||||
|
||||
rs.forEach((e) => {
|
||||
if (e.name) {
|
||||
if (e.name && e.meta?.dynamic) {
|
||||
router.removeRoute(e.name);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { isArray, orderBy } from "lodash-es";
|
||||
import { isArray, isNumber, isString, orderBy } from "lodash-es";
|
||||
import { resolveComponent } from "vue";
|
||||
import storage from "./storage";
|
||||
|
||||
// 首字母大写
|
||||
|
@ -292,6 +293,21 @@ export function mergeService(list: any[]) {
|
|||
return data;
|
||||
}
|
||||
|
||||
// 是否是组件
|
||||
export function isComponent(name: string) {
|
||||
return !isString(resolveComponent(name));
|
||||
}
|
||||
|
||||
// 是否Promise
|
||||
export function isPromise(val: any) {
|
||||
return val && Object.prototype.toString.call(val) === "[object Promise]";
|
||||
}
|
||||
|
||||
// 单位转换
|
||||
export function parsePx(val: string | number) {
|
||||
return isNumber(val) ? `${val}px` : val;
|
||||
}
|
||||
|
||||
export { storage };
|
||||
export * from "./data";
|
||||
export * from "./loading";
|
||||
|
|
|
@ -1,8 +1,2 @@
|
|||
import { resize } from "./resize";
|
||||
|
||||
window.onload = function () {
|
||||
resize();
|
||||
};
|
||||
|
||||
export * from "./theme";
|
||||
export * from "./permission";
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
import { useEventListener } from "@vueuse/core";
|
||||
import { useStore } from "../store";
|
||||
|
||||
function update() {
|
||||
const { app } = useStore();
|
||||
app.setBrowser();
|
||||
app.isFold = app.browser.isMini;
|
||||
}
|
||||
|
||||
export function resize() {
|
||||
useEventListener(window, "resize", update);
|
||||
update();
|
||||
}
|
|
@ -12,8 +12,8 @@
|
|||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, PropType } from "vue";
|
||||
import { isNumber } from "lodash-es";
|
||||
import { User } from "@element-plus/icons-vue";
|
||||
import { parsePx } from "/@/cool/utils";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-avatar",
|
||||
|
@ -44,7 +44,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
setup(props) {
|
||||
const size = isNumber(props.size) ? props.size + "px" : props.size;
|
||||
const size = parsePx(props.size);
|
||||
|
||||
const style = computed(() => {
|
||||
return {
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
<template>
|
||||
<div class="cl-codemirror" :class="{ disabled }">
|
||||
<codemirror
|
||||
ref="Editor"
|
||||
v-model="content"
|
||||
:placeholder="placeholder"
|
||||
:style="style"
|
||||
autofocus
|
||||
:disabled="disabled"
|
||||
indent-with-tab
|
||||
:tab-size="4"
|
||||
:extensions="extensions"
|
||||
@change="onChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { Codemirror } from "vue-codemirror";
|
||||
import { javascript } from "@codemirror/lang-javascript";
|
||||
import { oneDark } from "@codemirror/theme-one-dark";
|
||||
import { ref, watch, computed, defineComponent } from "vue";
|
||||
import { useDark } from "@vueuse/core";
|
||||
import { useComm } from "/@/cool";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-codemirror",
|
||||
|
||||
components: {
|
||||
Codemirror
|
||||
},
|
||||
|
||||
props: {
|
||||
modelValue: String,
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "请输入"
|
||||
},
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 400
|
||||
},
|
||||
fontSize: {
|
||||
type: [String, Number],
|
||||
default: 14
|
||||
},
|
||||
disabled: Boolean
|
||||
},
|
||||
|
||||
emits: ["update:modelValue", "change"],
|
||||
|
||||
setup(props, { emit }) {
|
||||
const { px } = useComm();
|
||||
|
||||
const Editor = ref();
|
||||
|
||||
// 是否暗黑模式
|
||||
const isDark = ref(useDark());
|
||||
|
||||
// 插件
|
||||
const extensions: any[] = [javascript()];
|
||||
|
||||
if (isDark.value) {
|
||||
extensions.push(oneDark);
|
||||
}
|
||||
|
||||
// 内容
|
||||
const content = ref("");
|
||||
|
||||
// 样式
|
||||
const style = computed(() => {
|
||||
return { height: px(props.height), fontSize: px(props.fontSize) };
|
||||
});
|
||||
|
||||
// 值改变
|
||||
function onChange(value: string) {
|
||||
emit("update:modelValue", value);
|
||||
emit("change", value);
|
||||
}
|
||||
|
||||
// 监听值
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(val) => {
|
||||
content.value = val || "";
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
return {
|
||||
Editor,
|
||||
isDark,
|
||||
style,
|
||||
content,
|
||||
extensions,
|
||||
onChange
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cl-codemirror {
|
||||
border: 1px solid var(--el-border-color);
|
||||
box-sizing: border-box;
|
||||
|
||||
:deep(.cm-editor) {
|
||||
.cm-foldGutter {
|
||||
width: 12px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
&.cm-focused {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
:deep(.cm-content) {
|
||||
background-color: var(--el-disabled-bg-color);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -27,8 +27,7 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineComponent, onMounted, ref } from "vue";
|
||||
import { useCrud } from "@cool-vue/crud";
|
||||
import { computed, defineComponent, onMounted, ref, PropType, nextTick } from "vue";
|
||||
import store from "store";
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -37,16 +36,13 @@ export default defineComponent({
|
|||
props: {
|
||||
name: String,
|
||||
columns: {
|
||||
type: Array,
|
||||
type: Array as PropType<any[]>,
|
||||
required: true,
|
||||
default: () => []
|
||||
}
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
// cl-crud
|
||||
const Crud = useCrud();
|
||||
|
||||
// 是否可见
|
||||
const visible = ref(false);
|
||||
|
||||
|
@ -59,8 +55,8 @@ export default defineComponent({
|
|||
// 列数据
|
||||
const list = computed(() => {
|
||||
return props.columns
|
||||
.filter((e: any) => !e.type)
|
||||
.map((e: any) => {
|
||||
.filter((e) => !e.type && e.prop)
|
||||
.map((e) => {
|
||||
return {
|
||||
label: e.label,
|
||||
value: e.prop
|
||||
|
@ -76,17 +72,17 @@ export default defineComponent({
|
|||
|
||||
// 改变列
|
||||
function change() {
|
||||
const names = getNames();
|
||||
nextTick(() => {
|
||||
const names = getNames();
|
||||
|
||||
if (store.get(name)) {
|
||||
Crud.value!["cl-table"].reBuild(() => {
|
||||
props.columns.map((e: any) => {
|
||||
if (store.get(name)) {
|
||||
props.columns.map((e) => {
|
||||
if (!e.type) {
|
||||
e.hidden = !names.includes(e.prop);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 保存
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
import { computed, defineComponent } from "vue";
|
||||
import { isArray, isNumber, isString } from "lodash-es";
|
||||
import { PictureFilled } from "@element-plus/icons-vue";
|
||||
import { useComm } from "/@/cool";
|
||||
import { parsePx } from "/@/cool/utils";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-image",
|
||||
|
@ -58,8 +58,6 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
setup(props) {
|
||||
const { px } = useComm();
|
||||
|
||||
const urls = computed(() => {
|
||||
const urls: any = props.modelValue || props.src;
|
||||
|
||||
|
@ -78,8 +76,8 @@ export default defineComponent({
|
|||
const [h, w]: any = isNumber(props.size) ? [props.size, props.size] : props.size;
|
||||
|
||||
return {
|
||||
h: px(h),
|
||||
w: px(w)
|
||||
h: parsePx(h),
|
||||
w: parsePx(w)
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -1,18 +1,5 @@
|
|||
<template>
|
||||
<el-select v-model="value" :clearable="clearable" @change="onChange">
|
||||
<el-option
|
||||
v-for="(item, index) in list"
|
||||
:key="index"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:disabled="item.disabled"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { useCrud } from "@cool-vue/crud";
|
||||
import { computed, defineComponent, isRef, ref, watch } from "vue";
|
||||
import { computed, defineComponent, isRef, Ref, ref, watch } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-select",
|
||||
|
@ -23,10 +10,6 @@ export default defineComponent({
|
|||
type: [Array, Object],
|
||||
default: () => []
|
||||
},
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
prop: String
|
||||
},
|
||||
|
||||
|
@ -40,9 +23,9 @@ export default defineComponent({
|
|||
const value = ref();
|
||||
|
||||
// 列表
|
||||
const list = computed<any>(() =>
|
||||
const list = computed(() =>
|
||||
isRef(props.options) ? props.options.value : props.options
|
||||
);
|
||||
) as Ref<any[]>;
|
||||
|
||||
// 值改变
|
||||
function onChange(val: string) {
|
||||
|
@ -64,11 +47,14 @@ export default defineComponent({
|
|||
}
|
||||
);
|
||||
|
||||
return {
|
||||
list,
|
||||
value,
|
||||
onChange
|
||||
return () => {
|
||||
return (
|
||||
<el-select v-model={value.value} clearable filterable onChange={onChange}>
|
||||
{list.value.map((e) => {
|
||||
return <el-option {...e} />;
|
||||
})}
|
||||
</el-select>
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
|
@ -71,20 +71,15 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
return {
|
||||
status,
|
||||
onChange
|
||||
return () => {
|
||||
return (
|
||||
<el-switch
|
||||
v-model={status.value}
|
||||
active-value={props.activeValue}
|
||||
inactive-value={props.inactiveValue}
|
||||
onChange={onChange}
|
||||
/>
|
||||
);
|
||||
};
|
||||
},
|
||||
|
||||
render(ctx: any) {
|
||||
return (
|
||||
<el-switch
|
||||
v-model={ctx.status}
|
||||
active-value={ctx.activeValue}
|
||||
inactive-value={ctx.inactiveValue}
|
||||
onChange={ctx.onChange}
|
||||
/>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,20 +1,27 @@
|
|||
<template>
|
||||
<div class="cl-view-group">
|
||||
<div class="cl-view-group" :class="[isExpand ? 'is-expand' : 'is-collapse']">
|
||||
<div class="cl-view-group__wrap">
|
||||
<!-- 组 -->
|
||||
<div class="cl-view-group__left" :class="[isExpand ? '_expand' : '_collapse']">
|
||||
<div class="cl-view-group__left">
|
||||
<slot name="left"></slot>
|
||||
|
||||
<!-- 收起按钮 -->
|
||||
<div class="collapse-btn" @click="expand(false)" v-if="browser.isMini">
|
||||
<el-icon>
|
||||
<arrow-right />
|
||||
</el-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容 -->
|
||||
<div class="cl-view-group__right">
|
||||
<div class="cl-view-group__right-head">
|
||||
<div class="icon" @click="toExpand()">
|
||||
<div class="icon" @click="expand()">
|
||||
<el-icon v-if="isExpand"><arrow-left /></el-icon>
|
||||
<el-icon v-else><arrow-right /></el-icon>
|
||||
</div>
|
||||
|
||||
<span>{{ title }}</span>
|
||||
<span>{{ label }}</span>
|
||||
</div>
|
||||
|
||||
<div class="cl-view-group__right-content">
|
||||
|
@ -28,7 +35,7 @@
|
|||
<script lang="ts">
|
||||
import { defineComponent, provide, ref, watch } from "vue";
|
||||
import { ArrowLeft, ArrowRight } from "@element-plus/icons-vue";
|
||||
import { useStore } from "../../store";
|
||||
import { useBrowser } from "/@/cool";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-view-group",
|
||||
|
@ -39,47 +46,67 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
props: {
|
||||
title: String
|
||||
title: String,
|
||||
leftWidth: {
|
||||
type: String,
|
||||
default: "300px"
|
||||
}
|
||||
},
|
||||
|
||||
setup() {
|
||||
const { app } = useStore();
|
||||
setup(props) {
|
||||
const { browser, onScreenChange } = useBrowser();
|
||||
|
||||
// 是否展开
|
||||
const isExpand = ref(true);
|
||||
|
||||
// 选中值
|
||||
const selected = ref();
|
||||
|
||||
// 标题
|
||||
const label = ref(props.title);
|
||||
|
||||
// 收起、展开
|
||||
function toExpand(value?: boolean) {
|
||||
function expand(value?: boolean) {
|
||||
isExpand.value = value === undefined ? !isExpand.value : value;
|
||||
}
|
||||
|
||||
// 小屏幕下
|
||||
function checkExpand(value?: boolean) {
|
||||
if (app.browser.isMini) {
|
||||
toExpand(value);
|
||||
// 设置选中值
|
||||
function select(data?: any) {
|
||||
selected.value = data;
|
||||
|
||||
if (browser.isMini) {
|
||||
expand(false);
|
||||
}
|
||||
}
|
||||
|
||||
// 监听屏幕大小变化
|
||||
watch(
|
||||
() => app.browser.isMini,
|
||||
(val: boolean) => {
|
||||
isExpand.value = !val;
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
// 设置标题
|
||||
function setTitle(value?: string) {
|
||||
label.value = value || "";
|
||||
}
|
||||
|
||||
provide("viewGroup", {
|
||||
checkExpand
|
||||
// 监听屏幕变化
|
||||
onScreenChange(() => {
|
||||
expand(!browser.isMini);
|
||||
});
|
||||
|
||||
return {
|
||||
// 监听标题
|
||||
watch(() => props.title, setTitle, {
|
||||
immediate: true
|
||||
});
|
||||
|
||||
const ctx = {
|
||||
label,
|
||||
selected,
|
||||
isExpand,
|
||||
toExpand,
|
||||
checkExpand
|
||||
setTitle,
|
||||
expand,
|
||||
select,
|
||||
browser
|
||||
};
|
||||
|
||||
provide("viewGroup", ctx);
|
||||
|
||||
return ctx;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -89,6 +116,8 @@ export default defineComponent({
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
$left-width: v-bind("leftWidth");
|
||||
|
||||
&__wrap {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
|
@ -98,25 +127,39 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
&__left {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
width: 300px;
|
||||
transition: width 0.3s;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
border-right: 1px solid var(--el-border-color);
|
||||
box-sizing: border-box;
|
||||
width: $left-width;
|
||||
background-color: #fff;
|
||||
|
||||
&._collapse {
|
||||
margin-right: 0;
|
||||
width: 0 !important;
|
||||
border-right: 0;
|
||||
.collapse-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
right: 20px;
|
||||
bottom: 30px;
|
||||
height: 40px;
|
||||
width: 40px;
|
||||
border-radius: 100%;
|
||||
background-color: var(--color-primary);
|
||||
box-shadow: 0 0 10px 1px #eee;
|
||||
|
||||
.el-icon {
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__right {
|
||||
width: calc(100% - 310px);
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
transition: width 0.3s;
|
||||
|
||||
&-head {
|
||||
display: flex;
|
||||
|
@ -150,13 +193,29 @@ export default defineComponent({
|
|||
}
|
||||
}
|
||||
|
||||
&.is-expand {
|
||||
.cl-view-group__right {
|
||||
width: calc(100% - $left-width);
|
||||
border-left: 1px solid var(--el-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 768px) {
|
||||
.cl-view-group__left {
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
transition: width 0.2s;
|
||||
width: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.cl-view-group__right {
|
||||
width: calc(100% - 100px);
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
&.is-expand {
|
||||
.cl-view-group__left {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import "./static/css/index.scss";
|
|||
export default (): ModuleConfig => {
|
||||
return {
|
||||
order: 99,
|
||||
components: Object.values(import.meta.glob("./components/**/*")),
|
||||
components: Object.values(import.meta.glob("./components/**/*.{vue,tsx}")),
|
||||
views: [
|
||||
{
|
||||
path: "/my/info",
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
import { ref } from "vue";
|
||||
import { ClViewGroup } from "../types";
|
||||
import { useParent } from "/@/cool";
|
||||
|
||||
export function useViewGroup() {
|
||||
const ViewGroup = ref<ClViewGroup>();
|
||||
useParent("cl-view-group", ViewGroup);
|
||||
return { ViewGroup };
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
export * from "./component";
|
|
@ -7,4 +7,5 @@ export function useBase() {
|
|||
}
|
||||
|
||||
export * from "./common";
|
||||
export * from "./hooks";
|
||||
export * from "./types/index.d";
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import { h } from "vue";
|
||||
import { defineComponent, h } from "vue";
|
||||
import { useStore } from "../../store";
|
||||
import { Menu } from "../../types";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
export default {
|
||||
export default defineComponent({
|
||||
name: "b-menu",
|
||||
|
||||
setup() {
|
||||
const { router, route } = useCool();
|
||||
const { router, route, browser } = useCool();
|
||||
const { menu, app } = useStore();
|
||||
|
||||
// 页面跳转
|
||||
|
@ -16,89 +16,87 @@ export default {
|
|||
router.push(url);
|
||||
}
|
||||
|
||||
// 移动端点击收起左侧菜单
|
||||
if (app.browser.isMini) {
|
||||
// 小屏下点击收起左侧菜单
|
||||
if (browser.isMini) {
|
||||
app.fold(true);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
route,
|
||||
toView,
|
||||
menu
|
||||
};
|
||||
},
|
||||
// 渲染子菜单
|
||||
function renderMenu() {
|
||||
function deep(list: Menu.Item[], index: number) {
|
||||
return list
|
||||
.filter((e) => e.isShow)
|
||||
.map((e) => {
|
||||
let html = null;
|
||||
|
||||
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>
|
||||
);
|
||||
if (e.type == 0) {
|
||||
html = h(
|
||||
<el-sub-menu />,
|
||||
{
|
||||
index: String(e.id),
|
||||
key: e.id,
|
||||
popperClass: "app-slider__menu"
|
||||
},
|
||||
default() {
|
||||
return deep(e.children || [], index + 1);
|
||||
{
|
||||
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>
|
||||
);
|
||||
);
|
||||
} 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;
|
||||
});
|
||||
return html;
|
||||
});
|
||||
}
|
||||
|
||||
return deep(menu.list, 1);
|
||||
}
|
||||
|
||||
const children = deep(ctx.menu.list, 1);
|
||||
|
||||
return (
|
||||
<div class="app-slider__menu">
|
||||
<el-menu
|
||||
default-active={ctx.route.path}
|
||||
background-color="transparent"
|
||||
collapse-transition={true}
|
||||
collapse={app.browser.isMini ? false : app.isFold}
|
||||
onSelect={ctx.toView}
|
||||
>
|
||||
{children}
|
||||
</el-menu>
|
||||
</div>
|
||||
);
|
||||
return () => {
|
||||
return (
|
||||
<div class="app-slider__menu">
|
||||
<el-menu
|
||||
default-active={route.path}
|
||||
background-color="transparent"
|
||||
collapse-transition={true}
|
||||
collapse={browser.isMini ? false : app.isFold}
|
||||
onSelect={toView}
|
||||
>
|
||||
{renderMenu()}
|
||||
</el-menu>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -54,7 +54,7 @@ function toPath() {
|
|||
|
||||
// 移动到
|
||||
function scrollTo(left: number) {
|
||||
refs.value.scroller.scrollTo({
|
||||
refs.scroller.scrollTo({
|
||||
left,
|
||||
behavior: "smooth"
|
||||
});
|
||||
|
@ -62,10 +62,10 @@ function scrollTo(left: number) {
|
|||
|
||||
// 调整滚动位置
|
||||
function adScroll(index: number) {
|
||||
const el = refs.value[`item-${index}`];
|
||||
const el = refs[`item-${index}`];
|
||||
|
||||
if (el) {
|
||||
scrollTo(el.offsetLeft + el.clientWidth - refs.value.scroller.clientWidth);
|
||||
scrollTo(el.offsetLeft + el.clientWidth - refs.scroller.clientWidth);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="route-nav">
|
||||
<p v-if="app.browser.isMini" class="title">
|
||||
<p v-if="browser.isMini" class="title">
|
||||
{{ lastName }}
|
||||
</p>
|
||||
|
||||
|
@ -20,8 +20,8 @@ import { flattenDeep, last } from "lodash-es";
|
|||
import { useCool } from "/@/cool";
|
||||
import { useBase } from "/$/base";
|
||||
|
||||
const { route } = useCool();
|
||||
const { app, menu } = useBase();
|
||||
const { route, browser } = useCool();
|
||||
const { menu } = useBase();
|
||||
|
||||
// 数据列表
|
||||
const list = computed(() => {
|
||||
|
@ -58,21 +58,18 @@ const lastName = computed(() => last(list.value)?.name);
|
|||
.route-nav {
|
||||
white-space: nowrap;
|
||||
|
||||
.el-breadcrumb {
|
||||
:deep(.el-breadcrumb) {
|
||||
margin: 0 10px;
|
||||
|
||||
&__inner {
|
||||
.el-breadcrumb__inner {
|
||||
font-size: 13px;
|
||||
padding: 0 10px;
|
||||
font-weight: normal;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
margin-left: 5px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<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>
|
||||
<span v-if="!app.isFold || browser.isMini">{{ app.info.name }}</span>
|
||||
</div>
|
||||
|
||||
<div class="app-slider__container">
|
||||
|
@ -13,8 +13,10 @@
|
|||
|
||||
<script lang="ts" setup>
|
||||
import { useBase } from "/$/base";
|
||||
import { useBrowser } from "/@/cool";
|
||||
import BMenu from "./bmenu";
|
||||
|
||||
const { browser } = useBrowser();
|
||||
const { app } = useBase();
|
||||
|
||||
function toHome() {
|
||||
|
|
|
@ -62,7 +62,6 @@ import AMenu from "./amenu.vue";
|
|||
const { router, service } = useCool();
|
||||
const { user, app } = useBase();
|
||||
|
||||
// 跳转
|
||||
async function onCommand(name: string) {
|
||||
switch (name) {
|
||||
case "my":
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
</div>
|
||||
<p class="desc">一款快速开发后台权限管理系统</p>
|
||||
|
||||
<el-form label-position="top" class="form" :disabled="saving" size="large">
|
||||
<el-form label-position="top" class="form" :disabled="saving">
|
||||
<el-form-item label="用户名">
|
||||
<input
|
||||
v-model="form.username"
|
||||
|
@ -105,7 +105,7 @@ async function toLogin() {
|
|||
// 跳转
|
||||
router.push("/");
|
||||
} catch (err: any) {
|
||||
refs.value.captcha.refresh();
|
||||
refs.captcha.refresh();
|
||||
ElMessage.error(err.message);
|
||||
}
|
||||
|
||||
|
@ -178,6 +178,7 @@ async function toLogin() {
|
|||
-webkit-text-fill-color: #fff;
|
||||
font-size: 15px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.5);
|
||||
border-radius: 0;
|
||||
|
||||
&:-webkit-autofill {
|
||||
box-shadow: 0 0 0px 1000px transparent inset !important;
|
||||
|
@ -213,7 +214,9 @@ async function toLogin() {
|
|||
margin-top: 50px;
|
||||
|
||||
:deep(.el-button) {
|
||||
height: 40px;
|
||||
width: 140px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// customize style
|
||||
// customize
|
||||
.scroller1 {
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
|
@ -19,7 +19,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
// Element-plus theme
|
||||
// Element-plus
|
||||
.el-input-number {
|
||||
.el-input-number__decrease,
|
||||
.el-input-number__increase {
|
||||
|
@ -29,5 +29,7 @@
|
|||
}
|
||||
|
||||
.el-dialog {
|
||||
--el-dialog-border-radius: 10px !important;
|
||||
&:not(.is-fullscreen) {
|
||||
--el-dialog-border-radius: 10px !important;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,19 +1,18 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { reactive, ref } from "vue";
|
||||
import { config } from "/@/cool";
|
||||
import { deepMerge, getBrowser, storage } from "/@/cool/utils";
|
||||
import { config, useBrowser } from "/@/cool";
|
||||
import { deepMerge, storage } from "/@/cool/utils";
|
||||
|
||||
export const useAppStore = defineStore("app", function () {
|
||||
const { browser, onScreenChange } = useBrowser();
|
||||
|
||||
// 基本信息
|
||||
const info = reactive({
|
||||
...config.app
|
||||
});
|
||||
|
||||
// 浏览器信息
|
||||
const browser = ref(getBrowser());
|
||||
|
||||
// 是否折叠
|
||||
const isFold = ref(browser.value.isMini || false);
|
||||
const isFold = ref(false);
|
||||
|
||||
// 事件
|
||||
const events = reactive<{ [key: string]: any[] }>({
|
||||
|
@ -35,11 +34,6 @@ export const useAppStore = defineStore("app", function () {
|
|||
storage.set("__app__", info);
|
||||
}
|
||||
|
||||
// 设置浏览器信息
|
||||
function setBrowser() {
|
||||
browser.value = getBrowser();
|
||||
}
|
||||
|
||||
// 添加事件
|
||||
function addEvent(name: string, func: any) {
|
||||
if (func) {
|
||||
|
@ -47,14 +41,17 @@ export const useAppStore = defineStore("app", function () {
|
|||
}
|
||||
}
|
||||
|
||||
// 监听屏幕变化
|
||||
onScreenChange(() => {
|
||||
isFold.value = browser.isMini;
|
||||
});
|
||||
|
||||
return {
|
||||
info,
|
||||
browser,
|
||||
isFold,
|
||||
fold,
|
||||
events,
|
||||
set,
|
||||
setBrowser,
|
||||
addEvent
|
||||
};
|
||||
});
|
||||
|
|
|
@ -63,7 +63,7 @@ export const useUserStore = defineStore("user", function () {
|
|||
// 退出
|
||||
async function logout() {
|
||||
clear();
|
||||
// router.clear();
|
||||
router.clear();
|
||||
router.push("/login");
|
||||
}
|
||||
|
||||
|
|
|
@ -39,3 +39,16 @@ export declare namespace Process {
|
|||
|
||||
type List = Item[];
|
||||
}
|
||||
|
||||
export declare interface ClViewGroup {
|
||||
selected:
|
||||
| {
|
||||
id?: number;
|
||||
[key: string]: any;
|
||||
}
|
||||
| undefined;
|
||||
isExpand: boolean;
|
||||
setTitle(value?: string): void;
|
||||
select(data?: any): void;
|
||||
expand(value?: boolean): void;
|
||||
}
|
||||
|
|
|
@ -11,18 +11,13 @@ import { useCrud, useForm } from "@cool-vue/crud";
|
|||
import DeptSelect from "./select.vue";
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
// cl-form
|
||||
const Form = useForm();
|
||||
|
||||
// cl-crud
|
||||
const Crud = useCrud();
|
||||
|
||||
// 打开
|
||||
async function open(ids: any[]) {
|
||||
Form.value?.open({
|
||||
title: "部门转移",
|
||||
width: "600px",
|
||||
width: "500px",
|
||||
props: {
|
||||
labelWidth: "80px"
|
||||
},
|
||||
|
@ -42,7 +37,7 @@ async function open(ids: any[]) {
|
|||
return done();
|
||||
}
|
||||
|
||||
ElMessageBox.confirm("是否转移部门?", "提示", {
|
||||
ElMessageBox.confirm("转移到新部门,是否继续?", "提示", {
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</el-tooltip>
|
||||
</li>
|
||||
|
||||
<li v-if="drag && !app.browser.isMini" @click="isDrag = true">
|
||||
<li v-if="drag && !browser.isMini" @click="isDrag = true">
|
||||
<el-tooltip content="拖动排序">
|
||||
<el-icon>
|
||||
<operation />
|
||||
|
@ -48,13 +48,13 @@
|
|||
<span
|
||||
class="dept-tree__node-label"
|
||||
:class="{
|
||||
'is-active': data.id == info?.id
|
||||
'is-active': data.id == ViewGroup?.selected?.id
|
||||
}"
|
||||
@click="rowClick(data)"
|
||||
>{{ node.label }}</span
|
||||
>
|
||||
<span
|
||||
v-if="app.browser.isMini"
|
||||
v-if="browser.isMini"
|
||||
class="dept-tree__node-icon"
|
||||
@click="onContextMenu($event, data, node)"
|
||||
>
|
||||
|
@ -73,14 +73,14 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" name="dept-tree" setup>
|
||||
import { inject, onMounted, ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useBrowser, useCool } from "/@/cool";
|
||||
import { deepTree, revDeepTree } from "/@/cool/utils";
|
||||
import { isArray } from "lodash-es";
|
||||
import { ContextMenu, useForm } from "@cool-vue/crud";
|
||||
import { Refresh as RefreshIcon, Operation, MoreFilled } from "@element-plus/icons-vue";
|
||||
import { useBase, checkPerm } from "/$/base";
|
||||
import { checkPerm, useViewGroup } from "/$/base";
|
||||
|
||||
const props = defineProps({
|
||||
drag: {
|
||||
|
@ -93,28 +93,20 @@ const props = defineProps({
|
|||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["list-change", "row-click", "user-add"]);
|
||||
const emit = defineEmits(["list-change", "refresh", "user-add"]);
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
const { app } = useBase();
|
||||
|
||||
// 树形列表
|
||||
const list = ref<any[]>([]);
|
||||
|
||||
// 选中
|
||||
const info = ref();
|
||||
|
||||
// 加载中
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
// 是否能拖动
|
||||
const isDrag = ref<boolean>(false);
|
||||
|
||||
// cl-form
|
||||
const { service, browser } = useCool();
|
||||
const { ViewGroup } = useViewGroup();
|
||||
const Form = useForm();
|
||||
|
||||
const viewGroup = inject<any>("viewGroup");
|
||||
// 树形列表
|
||||
const list = ref<Eps.BaseSysDepartmentEntity[]>([]);
|
||||
|
||||
// 加载中
|
||||
const loading = ref(false);
|
||||
|
||||
// 是否能拖动
|
||||
const isDrag = ref(false);
|
||||
|
||||
// 允许托的规则
|
||||
function allowDrag({ data }: any) {
|
||||
|
@ -131,34 +123,39 @@ async function refresh() {
|
|||
loading.value = true;
|
||||
isDrag.value = false;
|
||||
|
||||
await service.base.sys.department.list().then((res: any[]) => {
|
||||
await service.base.sys.department.list().then((res) => {
|
||||
list.value = deepTree(res);
|
||||
|
||||
if (!info.value) {
|
||||
info.value = list.value[0];
|
||||
if (!ViewGroup.value?.selected) {
|
||||
rowClick();
|
||||
}
|
||||
|
||||
// 模拟点击
|
||||
rowClick(info.value);
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 获取 ids
|
||||
function rowClick(e: any) {
|
||||
if (e) {
|
||||
const ids = e.children ? revDeepTree(e.children).map((e) => e.id) : [];
|
||||
ids.unshift(e.id);
|
||||
info.value = e;
|
||||
viewGroup.checkExpand(false);
|
||||
emit("row-click", { item: e, ids });
|
||||
function rowClick(item?: Eps.BaseSysDepartmentEntity) {
|
||||
if (!item) {
|
||||
item = list.value[0];
|
||||
}
|
||||
|
||||
if (item) {
|
||||
const ids = item.children ? revDeepTree(item.children).map((e) => e.id) : [];
|
||||
ids.unshift(item.id);
|
||||
|
||||
// 刷新列表
|
||||
emit("refresh", { page: 1, departmentIds: ids });
|
||||
|
||||
// 选择
|
||||
ViewGroup.value?.select(item);
|
||||
ViewGroup.value?.setTitle(`成员列表(${item.name})`);
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑部门
|
||||
function rowEdit(e: any) {
|
||||
const method = e.id ? "update" : "add";
|
||||
function rowEdit(item: Eps.BaseSysDepartmentEntity) {
|
||||
const method = item.id ? "update" : "add";
|
||||
|
||||
Form.value?.open({
|
||||
title: "编辑部门",
|
||||
|
@ -198,12 +195,14 @@ function rowEdit(e: any) {
|
|||
}
|
||||
}
|
||||
],
|
||||
form: e,
|
||||
form: {
|
||||
...item
|
||||
},
|
||||
on: {
|
||||
submit(data, { done, close }) {
|
||||
service.base.sys.department[method]({
|
||||
id: e.id,
|
||||
parentId: e.parentId,
|
||||
id: item.id,
|
||||
parentId: item.parentId,
|
||||
name: data.name,
|
||||
orderNum: data.orderNum
|
||||
})
|
||||
|
@ -222,23 +221,24 @@ function rowEdit(e: any) {
|
|||
}
|
||||
|
||||
// 删除部门
|
||||
function rowDel(e: any) {
|
||||
function rowDel(item: Eps.BaseSysDepartmentEntity) {
|
||||
async function del(f: boolean) {
|
||||
await service.base.sys.department
|
||||
.delete({
|
||||
ids: [e.id],
|
||||
ids: [item.id],
|
||||
deleteUser: f
|
||||
})
|
||||
.then(() => {
|
||||
if (e.id == info.value.id) {
|
||||
info.value = null;
|
||||
// 删除当前
|
||||
if (ViewGroup.value?.selected?.id == item.id) {
|
||||
rowClick();
|
||||
}
|
||||
|
||||
if (f) {
|
||||
ElMessage.success("删除成功");
|
||||
} else {
|
||||
ElMessageBox.confirm(
|
||||
`“${e.name}” 部门的用户已成功转移到 “${e.parentName}” 部门。`,
|
||||
`“${item.name}” 部门的用户已成功转移到 “${item.parentName}” 部门。`,
|
||||
"删除成功"
|
||||
);
|
||||
}
|
||||
|
@ -247,7 +247,7 @@ function rowDel(e: any) {
|
|||
refresh();
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(`该操作会删除 “${e.name}” 部门的所有用户,是否确认?`, "提示", {
|
||||
ElMessageBox.confirm(`该操作会删除 “${item.name}” 部门的所有用户,是否确认?`, "提示", {
|
||||
type: "warning",
|
||||
confirmButtonText: "直接删除",
|
||||
cancelButtonText: "保留用户",
|
||||
|
@ -256,7 +256,7 @@ function rowDel(e: any) {
|
|||
.then(() => {
|
||||
del(true);
|
||||
})
|
||||
.catch((action: string) => {
|
||||
.catch((action) => {
|
||||
if (action == "cancel") {
|
||||
del(false);
|
||||
}
|
||||
|
@ -449,11 +449,15 @@ onMounted(function () {
|
|||
}
|
||||
|
||||
&-icon {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
line-height: 28px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: #eee;
|
||||
height: 26px;
|
||||
width: 26px;
|
||||
text-align: center;
|
||||
margin-right: 5px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ watch(
|
|||
onMounted(() => {
|
||||
loading.value = true;
|
||||
|
||||
refs.value.iframe.onload = () => {
|
||||
refs.iframe.onload = () => {
|
||||
loading.value = false;
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-refresh-btn />
|
||||
|
||||
<el-button
|
||||
|
@ -22,17 +22,17 @@
|
|||
</cl-filter>
|
||||
|
||||
<cl-flex1 />
|
||||
<cl-search-key placeholder="请求地址、参数、ip" />
|
||||
</el-row>
|
||||
<cl-search-key placeholder="搜索请求地址、参数、ip" />
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-table ref="Table" :default-sort="{ prop: 'createTime', order: 'descending' }" />
|
||||
</el-row>
|
||||
<cl-row>
|
||||
<cl-table ref="Table" />
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<cl-pagination />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
</cl-crud>
|
||||
</template>
|
||||
|
||||
|
@ -45,7 +45,7 @@ import { useCrud, useTable } from "@cool-vue/crud";
|
|||
const { service } = useCool();
|
||||
|
||||
// 天数
|
||||
const day = ref<number>(1);
|
||||
const day = ref(1);
|
||||
|
||||
// cl-crud 配置
|
||||
const Crud = useCrud({ service: service.base.sys.log }, (app) => {
|
||||
|
@ -96,21 +96,26 @@ const Table = useTable({
|
|||
prop: "createTime",
|
||||
label: "创建时间",
|
||||
minWidth: 160,
|
||||
sortable: true
|
||||
sortable: "desc"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// 保存天数
|
||||
function saveDay() {
|
||||
service.base.sys.log.setKeep({ value: day.value }).then(() => {
|
||||
ElMessage.success("保存成功");
|
||||
});
|
||||
service.base.sys.log
|
||||
.setKeep({ value: day.value })
|
||||
.then(() => {
|
||||
ElMessage.success("保存成功");
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
// 清空日志
|
||||
function clear() {
|
||||
ElMessageBox.confirm("是否要清空日志", "提示", {
|
||||
ElMessageBox.confirm("是否要清空日志?", "提示", {
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
|
@ -129,7 +134,7 @@ function clear() {
|
|||
|
||||
onMounted(() => {
|
||||
// 获取天数
|
||||
service.base.sys.log.getKeep().then((res: number) => {
|
||||
service.base.sys.log.getKeep().then((res) => {
|
||||
day.value = Number(res);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-refresh-btn />
|
||||
<cl-add-btn />
|
||||
<menu-create v-if="isDev" />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-table ref="Table" row-key="id" @row-click="onRowClick">
|
||||
<!-- 名称 -->
|
||||
<template #column-name="{ scope }">
|
||||
|
@ -70,12 +70,7 @@
|
|||
>
|
||||
</template>
|
||||
</cl-table>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<cl-flex1 />
|
||||
<cl-pagination layout="total" />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<!-- 新增、编辑 -->
|
||||
<cl-upsert ref="Upsert">
|
||||
|
|
|
@ -1,34 +1,42 @@
|
|||
<template>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-refresh-btn />
|
||||
<cl-add-btn />
|
||||
<cl-multi-delete-btn />
|
||||
<cl-flex1 />
|
||||
<cl-search-key />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-table ref="Table" />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<cl-pagination />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<cl-upsert ref="Upsert">
|
||||
<template #slot-content="{ scope }">
|
||||
<div class="editor">
|
||||
<template v-for="(item, index) in tab.list" :key="index">
|
||||
<template v-if="tab.index == index">
|
||||
<el-button class="change-btn" @click="changeTab(item.to)">{{
|
||||
item.label
|
||||
}}</el-button>
|
||||
<div>
|
||||
<el-radio-group :model-value="tab.value" @change="onTabChange">
|
||||
<el-radio
|
||||
v-for="(item, index) in tab.list"
|
||||
:key="index"
|
||||
:label="item.value"
|
||||
>{{ item.label }}</el-radio
|
||||
>
|
||||
</el-radio-group>
|
||||
|
||||
<component :is="item.component" v-model="scope.data" />
|
||||
</template>
|
||||
</template>
|
||||
<el-input
|
||||
placeholder="请输入"
|
||||
v-model="scope.data"
|
||||
type="textarea"
|
||||
:rows="4"
|
||||
v-if="componentName == 'el-input'"
|
||||
/>
|
||||
<component :is="componentName" v-model="scope.data" v-else />
|
||||
</div>
|
||||
</template>
|
||||
</cl-upsert>
|
||||
|
@ -38,29 +46,30 @@
|
|||
<script lang="ts" name="sys-param" setup>
|
||||
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import { nextTick, reactive } from "vue";
|
||||
import { computed, reactive } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { isComponent } from "/@/cool/utils";
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
// 选项卡
|
||||
const tab = reactive<any>({
|
||||
index: null,
|
||||
|
||||
const tab = reactive({
|
||||
value: "el-input",
|
||||
list: [
|
||||
{
|
||||
label: "切换富文本编辑器",
|
||||
to: 1,
|
||||
component: "cl-codemirror"
|
||||
label: "代码编辑器",
|
||||
value: "cl-editor-monaco"
|
||||
},
|
||||
{
|
||||
label: "切换代码编辑器",
|
||||
to: 0,
|
||||
component: "cl-editor-wang"
|
||||
label: "富文本编辑器",
|
||||
value: "cl-editor-wang"
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const componentName = computed(() => {
|
||||
return isComponent(tab.value) ? tab.value : "el-input";
|
||||
});
|
||||
|
||||
// cl-crud 配置
|
||||
const Crud = useCrud({ service: service.base.sys.param }, (app) => {
|
||||
app.refresh();
|
||||
|
@ -153,41 +162,19 @@ const Upsert = useUpsert({
|
|||
],
|
||||
|
||||
onOpened(data) {
|
||||
tab.index = null;
|
||||
|
||||
nextTick(() => {
|
||||
if (Upsert.value?.mode == "add") {
|
||||
tab.index = 1;
|
||||
} else {
|
||||
tab.index = /<*>/g.test(data.data) ? 1 : 0;
|
||||
}
|
||||
});
|
||||
tab.value = /<*>/g.test(data.data) ? tab.list[1].value : tab.list[0].value;
|
||||
}
|
||||
});
|
||||
|
||||
// 切换编辑器
|
||||
function changeTab(i: number) {
|
||||
function onTabChange(name: any) {
|
||||
ElMessageBox.confirm("切换编辑器会清空输入内容,是否继续?", "提示", {
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
tab.index = i;
|
||||
tab.value = name;
|
||||
Upsert.value?.setForm("data", "");
|
||||
})
|
||||
.catch(() => null);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.change-btn {
|
||||
display: flex;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.editor {
|
||||
transition: all 0.3s;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-refresh-btn />
|
||||
<cl-add-btn />
|
||||
<cl-multi-delete-btn />
|
||||
<cl-flex1 />
|
||||
<cl-search-key />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-table
|
||||
ref="Table"
|
||||
:default-sort="{
|
||||
|
@ -16,12 +16,12 @@
|
|||
order: 'descending'
|
||||
}"
|
||||
/>
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<cl-pagination />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<cl-upsert ref="Upsert">
|
||||
<template #slot-relevance="{ scope }">
|
||||
|
|
|
@ -1,35 +1,28 @@
|
|||
<template>
|
||||
<cl-view-group :title="title">
|
||||
<cl-view-group ref="ViewGroup">
|
||||
<template #left>
|
||||
<dept-tree @row-click="onDeptRowClick" @user-add="onDeptUserAdd" />
|
||||
<dept-tree @refresh="refresh" @user-add="onUserAdd" />
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-refresh-btn />
|
||||
<cl-add-btn />
|
||||
<cl-multi-delete-btn />
|
||||
<el-button
|
||||
v-permission="service.base.sys.user.permission.move"
|
||||
type="success"
|
||||
:disabled="selects.ids.length == 0"
|
||||
:disabled="Table?.selection.length == 0"
|
||||
@click="toMove()"
|
||||
>转移</el-button
|
||||
>
|
||||
<cl-flex1 />
|
||||
<cl-search-key />
|
||||
</el-row>
|
||||
<cl-search-key placeholder="搜索用户名、姓名" />
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-table
|
||||
ref="Table"
|
||||
:default-sort="{
|
||||
prop: 'createTime',
|
||||
order: 'descending'
|
||||
}"
|
||||
@selection-change="onSelectionChange"
|
||||
>
|
||||
<cl-row>
|
||||
<cl-table ref="Table">
|
||||
<!-- 权限 -->
|
||||
<template #column-roleName="{ scope }">
|
||||
<template v-if="scope.row.roleName">
|
||||
|
@ -56,12 +49,12 @@
|
|||
>
|
||||
</template>
|
||||
</cl-table>
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<cl-pagination />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<!-- 新增、编辑 -->
|
||||
<cl-upsert ref="Upsert" />
|
||||
|
@ -75,23 +68,13 @@
|
|||
|
||||
<script lang="ts" name="sys-user" setup>
|
||||
import { useTable, useUpsert, useCrud } from "@cool-vue/crud";
|
||||
import { computed, reactive } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useViewGroup } from "../hooks";
|
||||
import DeptMove from "./components/dept/move.vue";
|
||||
import DeptTree from "./components/dept/tree.vue";
|
||||
|
||||
const { service, refs, setRefs } = useCool();
|
||||
|
||||
// 选择项
|
||||
const selects = reactive<any>({
|
||||
dept: {},
|
||||
ids: []
|
||||
});
|
||||
|
||||
// 标题
|
||||
const title = computed(() => {
|
||||
return `成员列表(${selects.dept?.name || ""})`;
|
||||
});
|
||||
const { ViewGroup } = useViewGroup();
|
||||
|
||||
// cl-crud 配置
|
||||
const Crud = useCrud({
|
||||
|
@ -113,13 +96,13 @@ const Table = useTable({
|
|||
}
|
||||
},
|
||||
{
|
||||
prop: "name",
|
||||
label: "姓名",
|
||||
prop: "username",
|
||||
label: "用户名",
|
||||
minWidth: 150
|
||||
},
|
||||
{
|
||||
prop: "username",
|
||||
label: "用户名",
|
||||
prop: "name",
|
||||
label: "姓名",
|
||||
minWidth: 150
|
||||
},
|
||||
{
|
||||
|
@ -159,7 +142,7 @@ const Table = useTable({
|
|||
{
|
||||
prop: "createTime",
|
||||
label: "创建时间",
|
||||
sortable: "custom",
|
||||
sortable: "desc",
|
||||
minWidth: 160
|
||||
},
|
||||
{
|
||||
|
@ -299,63 +282,48 @@ const Upsert = useUpsert({
|
|||
onSubmit(data, { next }) {
|
||||
next({
|
||||
...data,
|
||||
departmentId: selects.dept?.id
|
||||
departmentId: ViewGroup.value?.selected?.id
|
||||
});
|
||||
},
|
||||
|
||||
async onOpen() {
|
||||
const list = await service.base.sys.role.list();
|
||||
|
||||
// 设置权限列表
|
||||
Upsert.value?.setOptions(
|
||||
"roleIdList",
|
||||
list.map((e) => {
|
||||
return {
|
||||
label: e.name || "",
|
||||
value: e.id
|
||||
};
|
||||
})
|
||||
);
|
||||
service.base.sys.role.list().then((res) => {
|
||||
Upsert.value?.setOptions(
|
||||
"roleIdList",
|
||||
res.map((e) => {
|
||||
return {
|
||||
label: e.name || "",
|
||||
value: e.id
|
||||
};
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// 刷新列表
|
||||
function refresh(params: any) {
|
||||
function refresh(params?: any) {
|
||||
Crud.value?.refresh(params);
|
||||
}
|
||||
|
||||
// 多选监听
|
||||
function onSelectionChange(selection: any[]) {
|
||||
selects.ids = selection.map((e) => e.id);
|
||||
}
|
||||
|
||||
// 部门选择监听
|
||||
function onDeptRowClick({ item, ids }: any) {
|
||||
selects.dept = item;
|
||||
|
||||
refresh({
|
||||
page: 1,
|
||||
departmentIds: ids
|
||||
});
|
||||
}
|
||||
|
||||
// 部门下新增成员
|
||||
function onDeptUserAdd(item: any) {
|
||||
// 新增成员
|
||||
function onUserAdd({ id }: Eps.BaseSysDepartmentEntity) {
|
||||
Crud.value?.rowAppend({
|
||||
departmentId: item.id
|
||||
departmentId: id
|
||||
});
|
||||
}
|
||||
|
||||
// 移动成员
|
||||
async function toMove(e?: any) {
|
||||
async function toMove(item?: Eps.BaseSysDepartmentEntity) {
|
||||
let ids = [];
|
||||
|
||||
if (!e) {
|
||||
ids = selects.ids;
|
||||
if (item) {
|
||||
ids = [item.id];
|
||||
} else {
|
||||
ids = [e.id];
|
||||
ids = Table.value?.selection.map((e) => e.id) || [];
|
||||
}
|
||||
|
||||
refs.value.deptMove.open(ids);
|
||||
refs.deptMove.open(ids);
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
height="630px"
|
||||
width="1200px"
|
||||
keep-alive
|
||||
:draggable="false"
|
||||
custom-class="cl-chat__dialog"
|
||||
:close-on-click-modal="false"
|
||||
close-on-press-escape
|
||||
append-to-body
|
||||
|
@ -23,7 +21,7 @@
|
|||
<div
|
||||
class="cl-chat"
|
||||
:class="{
|
||||
'is-mini': app.browser.isMini,
|
||||
'is-mini': browser.isMini,
|
||||
'is-expand': isExpand
|
||||
}"
|
||||
>
|
||||
|
@ -52,9 +50,9 @@
|
|||
</template>
|
||||
|
||||
<script lang="ts" name="cl-chat" setup>
|
||||
import { nextTick, provide, ref, watch } from "vue";
|
||||
import { nextTick, provide, ref } from "vue";
|
||||
import dayjs from "dayjs";
|
||||
import { useCool, config, module } from "/@/cool";
|
||||
import { useCool, config, module, useBrowser } from "/@/cool";
|
||||
import { useBase } from "/$/base";
|
||||
import { Notebook, ArrowLeft, BellFilled } from "@element-plus/icons-vue";
|
||||
import { debounce } from "lodash-es";
|
||||
|
@ -66,12 +64,13 @@ import { Chat } from "../types";
|
|||
import { useStore } from "../store";
|
||||
|
||||
const { mitt } = useCool();
|
||||
const { browser, onScreenChange } = useBrowser();
|
||||
|
||||
// 缓存
|
||||
const { session, message } = useStore();
|
||||
|
||||
// 缓存
|
||||
const { app, user } = useBase();
|
||||
const { user } = useBase();
|
||||
|
||||
// 模块配置
|
||||
const { options } = module.get("chat");
|
||||
|
@ -83,7 +82,7 @@ const visible = ref(false);
|
|||
const isExpand = ref(true);
|
||||
|
||||
// 未读消息
|
||||
const unCount = ref(parseInt(Math.random() * 100));
|
||||
const unCount = ref(parseInt(String(Math.random() * 100)));
|
||||
|
||||
// Socket
|
||||
let socket: Socket;
|
||||
|
@ -128,6 +127,11 @@ function close() {
|
|||
visible.value = false;
|
||||
}
|
||||
|
||||
// 收起、展开
|
||||
function expand(value?: boolean) {
|
||||
isExpand.value = value === undefined ? !isExpand.value : value;
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
function send(data: Chat.Message, isAppend?: boolean) {
|
||||
// socket.emit("message", {});
|
||||
|
@ -174,19 +178,15 @@ provide("chat", {
|
|||
return socket;
|
||||
},
|
||||
send,
|
||||
append,
|
||||
expand,
|
||||
scrollToBottom
|
||||
});
|
||||
|
||||
// 监听屏幕大小变化
|
||||
watch(
|
||||
() => app.browser.isMini,
|
||||
(val) => {
|
||||
isExpand.value = val ? false : true;
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
// 监听屏幕变化
|
||||
onScreenChange(() => {
|
||||
isExpand.value = browser.isMini ? false : true;
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
|
@ -212,12 +212,6 @@ defineExpose({
|
|||
}
|
||||
}
|
||||
|
||||
&__dialog {
|
||||
.el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
padding: 9px 0;
|
||||
}
|
||||
|
|
|
@ -140,7 +140,7 @@ const previewUrls = computed(() =>
|
|||
|
||||
// 文本消息
|
||||
function onTextSend() {
|
||||
chat?.send(
|
||||
chat.send(
|
||||
{
|
||||
contentType: 0,
|
||||
content: {
|
||||
|
@ -154,7 +154,7 @@ function onTextSend() {
|
|||
|
||||
// 图片消息
|
||||
function onImageSend(res: any) {
|
||||
chat?.send(
|
||||
chat.send(
|
||||
{
|
||||
contentType: 1,
|
||||
content: {
|
||||
|
|
|
@ -67,9 +67,10 @@ const list = computed(() => session?.list.filter((e) => e.nickName?.includes(key
|
|||
|
||||
// 会话详情
|
||||
async function toDetail(item: Chat.Session) {
|
||||
chat.expand(false);
|
||||
session.set(item);
|
||||
await message.get({ page: 1 });
|
||||
chat?.scrollToBottom();
|
||||
chat.scrollToBottom();
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ import { inject } from "vue";
|
|||
import { Chat } from "../types";
|
||||
|
||||
export function useChat() {
|
||||
const chat = inject<Chat.Provide>("chat");
|
||||
const chat = inject("chat") as Chat.Provide;
|
||||
|
||||
return {
|
||||
chat
|
||||
|
|
|
@ -30,6 +30,7 @@ export namespace Chat {
|
|||
socket?: Socket;
|
||||
send(data: Message, isAppend?: boolean): void;
|
||||
append(data: Message): void;
|
||||
expand(boolean?: boolean): void;
|
||||
scrollToBottom(): void;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<span>cl-editor-quill</span>
|
||||
Quill 富文本编辑器
|
||||
<span>cl-editor</span>
|
||||
编辑器
|
||||
</div>
|
||||
<div class="c">
|
||||
<router-link to="/editor-quill">传送门</router-link>
|
|
@ -0,0 +1,14 @@
|
|||
<template>
|
||||
<div class="scope">
|
||||
<div class="h">
|
||||
<span>file</span>
|
||||
文件列表
|
||||
</div>
|
||||
<div class="c">
|
||||
<router-link to="/upload/list">传送门</router-link>
|
||||
</div>
|
||||
<div class="f">
|
||||
<span class="date">2023/01/01</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
|
@ -3,8 +3,8 @@
|
|||
|
||||
<cl-form ref="Form">
|
||||
<template #slot-crud>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-crud ref="Crud" padding="0">
|
||||
<cl-row>
|
||||
<!-- 刷新按钮 -->
|
||||
<cl-refresh-btn />
|
||||
<!-- 新增按钮 -->
|
||||
|
@ -14,18 +14,18 @@
|
|||
<cl-flex1 />
|
||||
<!-- 关键字搜索 -->
|
||||
<cl-search-key />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<!-- 数据表格 -->
|
||||
<cl-table ref="Table" />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<!-- 分页控件 -->
|
||||
<cl-pagination />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<!-- 新增、编辑 -->
|
||||
<cl-upsert ref="Upsert" />
|
||||
|
@ -36,6 +36,9 @@
|
|||
|
||||
<script lang="ts" name="菜单名称" setup>
|
||||
import { useCrud, useForm, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { useCool } from "/@/cool";
|
||||
|
||||
const { refs, setRefs } = useCool();
|
||||
|
||||
// cl-upsert 配置
|
||||
const Upsert = useUpsert({
|
||||
|
@ -92,18 +95,34 @@ const Form = useForm();
|
|||
|
||||
function open() {
|
||||
Form.value?.open({
|
||||
title: "内嵌crud",
|
||||
title: "自定义表单",
|
||||
props: {
|
||||
labelPosition: "top"
|
||||
},
|
||||
items: [
|
||||
{
|
||||
label: "",
|
||||
props: {
|
||||
labelWidth: "0"
|
||||
},
|
||||
label: "获取 ref,打开后聚焦",
|
||||
prop: "name",
|
||||
component: {
|
||||
name: "el-input",
|
||||
ref: setRefs("name"),
|
||||
props: {
|
||||
placeholder: "请填写昵称"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "内嵌 cl-crud",
|
||||
component: {
|
||||
name: "slot-crud"
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
on: {
|
||||
open() {
|
||||
refs.name.focus();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-refresh-btn />
|
||||
<cl-add-btn />
|
||||
<form-crud />
|
||||
<cl-multi-delete-btn />
|
||||
<form-btn />
|
||||
|
||||
<cl-filter label="字典筛选">
|
||||
<cl-select :options="dict.get('brand')" prop="brand" />
|
||||
|
@ -12,28 +13,49 @@
|
|||
<cl-flex1 />
|
||||
<cl-column-custom :columns="Table?.columns" />
|
||||
<cl-search-key />
|
||||
</el-row>
|
||||
<cl-adv-btn />
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-table ref="Table" />
|
||||
</el-row>
|
||||
<cl-row>
|
||||
<cl-table ref="Table" show-summary :summary-method="onSummaryMethod">
|
||||
<template #column-detail="{ scope }">
|
||||
<div style="padding: 0 10px">展开信息 - {{ scope.row.name }}</div>
|
||||
</template>
|
||||
</cl-table>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
<cl-pagination />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<cl-upsert ref="Upsert" />
|
||||
<cl-adv-search ref="AdvSearch" />
|
||||
</cl-crud>
|
||||
</template>
|
||||
|
||||
<script lang="tsx" setup name="crud">
|
||||
import { useCrud, useUpsert, useTable } from "@cool-vue/crud";
|
||||
import { useCrud, useUpsert, useTable, useAdvSearch } from "@cool-vue/crud";
|
||||
import { useDict } from "/$/dict";
|
||||
import FormCrud from "../components/form-crud.vue";
|
||||
import FormBtn from "../components/form.vue";
|
||||
import { reactive } from "vue";
|
||||
|
||||
const { dict } = useDict();
|
||||
|
||||
const options = reactive({
|
||||
status: [
|
||||
{
|
||||
label: "开启",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "关闭",
|
||||
type: "danger",
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const Crud = useCrud(
|
||||
{
|
||||
service: "test"
|
||||
|
@ -43,19 +65,13 @@ const Crud = useCrud(
|
|||
}
|
||||
);
|
||||
|
||||
// 新增、编辑
|
||||
const Upsert = useUpsert({
|
||||
items: [
|
||||
{
|
||||
label: "姓名",
|
||||
prop: "name",
|
||||
required: true,
|
||||
component: {
|
||||
name: "el-input"
|
||||
}
|
||||
},
|
||||
{
|
||||
type: "tabs",
|
||||
props: {
|
||||
type: "card",
|
||||
labels: [
|
||||
{
|
||||
label: "基础",
|
||||
|
@ -68,6 +84,15 @@ const Upsert = useUpsert({
|
|||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "姓名",
|
||||
prop: "name",
|
||||
required: true,
|
||||
group: "base",
|
||||
component: {
|
||||
name: "el-input"
|
||||
}
|
||||
},
|
||||
{
|
||||
label: "认证类型",
|
||||
prop: "authType",
|
||||
|
@ -106,36 +131,39 @@ const Upsert = useUpsert({
|
|||
}
|
||||
});
|
||||
|
||||
// 表格
|
||||
const Table = useTable({
|
||||
columns: [
|
||||
{
|
||||
type: "selection"
|
||||
type: "selection",
|
||||
width: 60
|
||||
},
|
||||
() => {
|
||||
return {
|
||||
label: "#",
|
||||
type: "expand",
|
||||
prop: "detail"
|
||||
};
|
||||
},
|
||||
{
|
||||
label: "姓名",
|
||||
prop: "name"
|
||||
},
|
||||
{
|
||||
label: "存款",
|
||||
prop: "price",
|
||||
formatter(row) {
|
||||
return `¥${row.price}`;
|
||||
}
|
||||
label: "基础信息",
|
||||
prop: "baseInfo",
|
||||
children: [
|
||||
{
|
||||
label: "姓名",
|
||||
prop: "name"
|
||||
},
|
||||
{
|
||||
label: "存款(元)",
|
||||
prop: "price",
|
||||
sortable: true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
dict: [
|
||||
{
|
||||
label: "开启",
|
||||
value: 1
|
||||
},
|
||||
{
|
||||
label: "关闭",
|
||||
type: "danger",
|
||||
value: 0
|
||||
}
|
||||
]
|
||||
dict: options.status
|
||||
},
|
||||
{
|
||||
label: "创建时间",
|
||||
|
@ -149,4 +177,21 @@ const Table = useTable({
|
|||
}
|
||||
]
|
||||
});
|
||||
|
||||
function onSummaryMethod({ data }: { data: any[] }) {
|
||||
return ["合计", "", "", data.reduce((a, b) => parseFloat(a + Number(b.price)), 0).toFixed(2)];
|
||||
}
|
||||
|
||||
// 高级搜索
|
||||
const AdvSearch = useAdvSearch({
|
||||
items: [
|
||||
{
|
||||
label: "昵称",
|
||||
prop: "name",
|
||||
component: {
|
||||
name: "el-input"
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -12,11 +12,12 @@
|
|||
import ContextMenu from "../components/context-menu.vue";
|
||||
import Crud from "../components/crud.vue";
|
||||
import Upload from "../components/upload.vue";
|
||||
import EditorQuill from "../components/editor-quill.vue";
|
||||
import Editor from "../components/editor.vue";
|
||||
import Svg from "../components/svg.vue";
|
||||
import Copy from "../components/copy.vue";
|
||||
import File from "../components/file.vue";
|
||||
|
||||
const list = [ContextMenu, Crud, Upload, EditorQuill, Svg, Copy];
|
||||
const list = [ContextMenu, Crud, Upload, Editor, Svg, Copy, File];
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
|
|
@ -1,12 +1,16 @@
|
|||
<template>
|
||||
<div class="editor">
|
||||
<el-tabs type="card">
|
||||
<el-tab-pane label="WangEditor">
|
||||
<cl-editor-wang v-model="w" :height="400" />
|
||||
<el-tab-pane label="Wang">
|
||||
<cl-editor-wang v-model="v1" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="Quill" lazy>
|
||||
<cl-editor-quill v-model="q" :height="400" />
|
||||
<cl-editor-quill v-model="v2" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="Monaco" lazy>
|
||||
<cl-editor-monaco v-model="v3" language="typescript" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
@ -14,8 +18,9 @@
|
|||
|
||||
<script lang="ts" setup name="editor-quill">
|
||||
import { ref } from "vue";
|
||||
const q = ref("Quill");
|
||||
const w = ref("Wang");
|
||||
const v1 = ref("富文本编辑器 cl-editor-wang");
|
||||
const v2 = ref("富文本编辑器 cl-editor-quill");
|
||||
const v3 = ref(`// 代码编辑器\r\n const data = { name: "cl-editor-monaco" }`);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
|
@ -43,9 +43,9 @@
|
|||
|
||||
<div class="hot-search__table">
|
||||
<cl-crud ref="Crud" padding="0">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-table ref="Table" :border="false" />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
</cl-crud>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -2,19 +2,23 @@
|
|||
<div class="demo">
|
||||
<el-tabs type="card">
|
||||
<el-tab-pane label="普通上传">
|
||||
<cl-upload v-model="urls" />
|
||||
<cl-upload v-model="v1" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="多图上传" lazy>
|
||||
<cl-upload text="选择图片" v-model="urls" multiple drag />
|
||||
<cl-upload v-model="v2" text="选择图片" multiple />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="文件上传" lazy>
|
||||
<cl-upload v-model="urls" multiple text="文件上传" type="file" />
|
||||
<cl-upload v-model="v3" multiple text="文件上传" type="file" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="可拖拽">
|
||||
<cl-upload multiple draggable />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="自定义内容">
|
||||
<cl-upload text="选择图片" multiple drag custom-class="custom-upload">
|
||||
<cl-upload type="file" multiple draggable custom-class="custom-upload">
|
||||
<el-button :icon="Upload">上传</el-button>
|
||||
|
||||
<template #item="{ item }">
|
||||
|
@ -24,11 +28,19 @@
|
|||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="自定义大小">
|
||||
<cl-upload text="选择图片" :size="[120, 200]" />
|
||||
<cl-upload :size="[120, 200]" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="上传校验">
|
||||
<cl-upload :before-upload="onBeforeUpload" />
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="文件空间">
|
||||
<cl-upload-space />
|
||||
<cl-upload-space v-model="v4" :limit="3" @confirm="onConfirm" />
|
||||
|
||||
<div class="space-upload">
|
||||
<el-image fit="cover" v-for="(item, index) in v4" :key="index" :src="item" />
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
@ -37,8 +49,31 @@
|
|||
<script lang="ts" name="upload" setup>
|
||||
import { ref } from "vue";
|
||||
import { Upload } from "@element-plus/icons-vue";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const urls = ref("");
|
||||
const v1 = ref("");
|
||||
const v2 = ref(
|
||||
"https://show.cool-admin.com/api/public/uploads/20230106/903f6398-63ab-4a6b-b01b-2ae4c0fd4261_u=3956957950%2C1274376364&fm=253&fmt=auto&app=138&f=JPEG.webp,https://show.cool-admin.com/api/public/uploads/20230106/1c6e70a7-37f5-41c9-881e-46b625a9696a_u=672347980%2C1207046361&fm=253&fmt=auto&app=138&f=JPEG.webp,https://show.cool-admin.com/api/public/uploads/20230106/8c06206a-dc3e-48c0-8d13-7f7905a6dc6c_u=1536642037%2C1165863875&fm=253&fmt=auto&app=138&f=JPEG.webp,https://show.cool-admin.com/api/public/uploads/20230106/387e8865-4a29-4e91-b491-90a58a870469_src=http___img.zcool.cn_community_01bb3b590aa3c3a8012145509011b7.jpg@1280w_1l_2o_100sh.jpg&refer=http___img.zcool.webp,https://show.cool-admin.com/api/public/uploads/20230106/88ecaa19-7ab1-4354-bd0d-f755db322f9b_src=http___img.zcool.cn_community_0113e05efed248a801215aa0326aec.jpg@2o.jpg&refer=http___img.zcool.webp,https://show.cool-admin.com/api/public/uploads/20230106/2a472e03-8a72-4d08-9171-74e3e3d4f3e3_src=http___img.zcool.cn_community_0120d15e01c068a80120a895b5bf80.png@1280w_1l_2o_100sh.png&refer=http___img.zcool.webp"
|
||||
);
|
||||
const v3 = ref("");
|
||||
const v4 = ref<string[]>([]);
|
||||
|
||||
function onConfirm(list: any[]) {
|
||||
v4.value = list.map((e) => e.url);
|
||||
}
|
||||
|
||||
function onBeforeUpload(file: any, item: any) {
|
||||
console.log(file, item);
|
||||
ElMessage.warning("文件检测中");
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
ElMessage.success("文件检测通过");
|
||||
|
||||
resolve(true);
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -53,14 +88,20 @@ const urls = ref("");
|
|||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 3px;
|
||||
padding: 5px 10px;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.cl-upload__list {
|
||||
width: 100%;
|
||||
.space-upload {
|
||||
margin-top: 10px;
|
||||
|
||||
.el-image {
|
||||
margin-right: 10px;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
:key="index"
|
||||
class="item"
|
||||
:class="{
|
||||
'is-active': active == item.id
|
||||
'is-active': ViewGroup?.selected?.id == item.id
|
||||
}"
|
||||
@click="select(item)"
|
||||
@contextmenu="
|
||||
|
@ -30,7 +30,9 @@
|
|||
"
|
||||
>
|
||||
<span>{{ item.name }} - {{ item.key }}</span>
|
||||
<el-icon v-show="active == item.id"><arrow-right-bold /></el-icon>
|
||||
<el-icon v-show="ViewGroup?.selected?.id == item.id"
|
||||
><arrow-right-bold
|
||||
/></el-icon>
|
||||
</li>
|
||||
|
||||
<el-empty v-if="list.length == 0" :image-size="80" />
|
||||
|
@ -46,37 +48,32 @@
|
|||
<script lang="ts" setup>
|
||||
import { ContextMenu, useForm } from "@cool-vue/crud";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { inject, onMounted, ref } from "vue";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { ArrowRightBold } from "@element-plus/icons-vue";
|
||||
import { checkPerm } from "/$/base";
|
||||
import { checkPerm, useViewGroup } from "/$/base";
|
||||
|
||||
const emit = defineEmits(["refresh"]);
|
||||
|
||||
const { service } = useCool();
|
||||
const { ViewGroup } = useViewGroup();
|
||||
const Form = useForm();
|
||||
|
||||
// 字典
|
||||
const dict = inject<any>("dict");
|
||||
|
||||
// 选中
|
||||
const active = ref("");
|
||||
|
||||
// 列表
|
||||
const list = ref<any[]>([]);
|
||||
const list = ref<Eps.DictTypeEntity[]>([]);
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
|
||||
const viewGroup = inject<any>("viewGroup");
|
||||
|
||||
// 刷新
|
||||
async function refresh() {
|
||||
loading.value = true;
|
||||
|
||||
await service.dict.type.list().then((res) => {
|
||||
await service.dict.type.list({ order: "createTime", sort: "asc" }).then((res) => {
|
||||
list.value = res;
|
||||
|
||||
if (!active.value) {
|
||||
select(res[0]);
|
||||
if (!ViewGroup.value?.selected) {
|
||||
select();
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -84,23 +81,24 @@ async function refresh() {
|
|||
}
|
||||
|
||||
// 选择
|
||||
function select(item: any) {
|
||||
active.value = item?.id;
|
||||
function select(item?: Eps.DictTypeEntity) {
|
||||
if (!item) {
|
||||
item = list.value[0];
|
||||
}
|
||||
|
||||
// 设置
|
||||
dict.setGroup(item);
|
||||
if (item) {
|
||||
emit("refresh", {
|
||||
typeId: item.id,
|
||||
page: 1
|
||||
});
|
||||
|
||||
// 刷新
|
||||
dict.refresh({
|
||||
typeId: active.value,
|
||||
page: 1
|
||||
});
|
||||
|
||||
viewGroup.checkExpand(false);
|
||||
ViewGroup.value?.select(item);
|
||||
ViewGroup.value?.setTitle(`字典列表(${item.name})`);
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑
|
||||
function edit(item?: any) {
|
||||
function edit(item?: Eps.DictTypeEntity) {
|
||||
Form.value?.open({
|
||||
title: item ? "编辑类型" : "添加类型",
|
||||
width: "500px",
|
||||
|
@ -152,7 +150,7 @@ function edit(item?: any) {
|
|||
}
|
||||
|
||||
// 右键
|
||||
function onContextMenu(e: any, item: any) {
|
||||
function onContextMenu(e: any, item: Eps.DictTypeEntity) {
|
||||
ContextMenu.open(e, {
|
||||
hover: {
|
||||
target: "item"
|
||||
|
@ -187,8 +185,8 @@ function onContextMenu(e: any, item: any) {
|
|||
await refresh();
|
||||
|
||||
// 删除当前
|
||||
if (active.value == item.id) {
|
||||
select(list.value[0]);
|
||||
if (ViewGroup.value?.selected?.id == item.id) {
|
||||
select();
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
|
@ -225,42 +223,45 @@ onMounted(() => {
|
|||
|
||||
.list {
|
||||
height: calc(100% - 40px);
|
||||
padding: 10px;
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
ul {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
list-style: none;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 35px 10px 10px;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 3px;
|
||||
color: #666;
|
||||
position: relative;
|
||||
background-color: #f7f7f7;
|
||||
ul {
|
||||
height: 100%;
|
||||
|
||||
.el-icon {
|
||||
position: absolute;
|
||||
right: 10px !important;
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
list-style: none;
|
||||
box-sizing: border-box;
|
||||
padding: 10px 35px 10px 10px;
|
||||
cursor: pointer;
|
||||
font-size: 13px;
|
||||
margin-bottom: 10px;
|
||||
border-radius: 3px;
|
||||
color: #666;
|
||||
position: relative;
|
||||
background-color: #f7f7f7;
|
||||
|
||||
.el-icon {
|
||||
position: absolute;
|
||||
right: 10px !important;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.is-active {
|
||||
background-color: var(--color-primary);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
opacity: 0.8;
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,32 +1,24 @@
|
|||
<template>
|
||||
<cl-view-group :title="title">
|
||||
<cl-view-group ref="ViewGroup">
|
||||
<template #left>
|
||||
<dict-group />
|
||||
<dict-group @refresh="refresh" />
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<cl-crud ref="Crud">
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<!-- 刷新按钮 -->
|
||||
<cl-refresh-btn />
|
||||
<!-- 新增按钮 -->
|
||||
<cl-add-btn :disabled="!group" />
|
||||
<cl-add-btn :disabled="!ViewGroup?.selected" />
|
||||
<cl-flex1 />
|
||||
<!-- 关键字搜索 -->
|
||||
<cl-search-key />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<!-- 数据表格 -->
|
||||
<cl-table
|
||||
ref="Table"
|
||||
:default-sort="{
|
||||
prop: 'orderNum',
|
||||
order: 'ascending'
|
||||
}"
|
||||
row-key="id"
|
||||
@row-click="onRowClick"
|
||||
>
|
||||
<cl-table ref="Table" row-key="id" @row-click="onRowClick">
|
||||
<template #slot-btn="{ scope }">
|
||||
<el-button
|
||||
text
|
||||
|
@ -38,11 +30,11 @@
|
|||
>
|
||||
</template>
|
||||
</cl-table>
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<el-row>
|
||||
<cl-row>
|
||||
<cl-flex1 />
|
||||
</el-row>
|
||||
</cl-row>
|
||||
|
||||
<!-- 新增、编辑 -->
|
||||
<cl-upsert ref="Upsert" />
|
||||
|
@ -55,19 +47,13 @@
|
|||
import { useCrud, useTable, useUpsert } from "@cool-vue/crud";
|
||||
import { useCool } from "/@/cool";
|
||||
import DictGroup from "../components/group.vue";
|
||||
import { computed, provide, ref } from "vue";
|
||||
import { computed } from "vue";
|
||||
import { deepTree } from "/@/cool/utils";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import { useViewGroup } from "/$/base";
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
// 组
|
||||
const group = ref();
|
||||
|
||||
// 标题
|
||||
const title = computed(() => {
|
||||
return group.value ? `字典列表(${group.value.name})` : "字典列表";
|
||||
});
|
||||
const { ViewGroup } = useViewGroup();
|
||||
|
||||
// cl-upsert 配置
|
||||
const Upsert = useUpsert({
|
||||
|
@ -141,7 +127,7 @@ const Upsert = useUpsert({
|
|||
onSubmit(data, { next }) {
|
||||
next({
|
||||
...data,
|
||||
typeId: group.value.id
|
||||
typeId: ViewGroup.value?.selected?.id
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -167,7 +153,7 @@ const Table = useTable({
|
|||
],
|
||||
columns: [
|
||||
{ label: "名称", prop: "name", align: "left", minWidth: 200 },
|
||||
{ label: "排序", prop: "orderNum", sortable: "custom", width: 100 },
|
||||
{ label: "排序", prop: "orderNum", sortable: "desc", width: 100 },
|
||||
{ label: "备注", prop: "remark", showOverflowTooltip: true, minWidth: 150 },
|
||||
{ label: "创建时间", prop: "createTime", sortable: "custom", minWidth: 160 },
|
||||
{ label: "更新时间", prop: "updateTime", sortable: "custom", minWidth: 160 },
|
||||
|
@ -185,7 +171,6 @@ const Crud = useCrud({
|
|||
onRefresh(params, { render }) {
|
||||
service.dict.info
|
||||
.list({
|
||||
typeId: group.value?.id,
|
||||
...params,
|
||||
page: undefined,
|
||||
size: undefined
|
||||
|
@ -201,11 +186,6 @@ function refresh(params?: any) {
|
|||
Crud.value?.refresh(params);
|
||||
}
|
||||
|
||||
// 设置组
|
||||
function setGroup(data: any) {
|
||||
group.value = data;
|
||||
}
|
||||
|
||||
// 行点击展开
|
||||
function onRowClick(row: any, column: any) {
|
||||
if (column?.property && row.children) {
|
||||
|
@ -220,10 +200,4 @@ function append(row: any) {
|
|||
orderNum: 1
|
||||
});
|
||||
}
|
||||
|
||||
provide("dict", {
|
||||
group,
|
||||
refresh,
|
||||
setGroup
|
||||
});
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
<template>
|
||||
<div
|
||||
class="cl-editor-monaco"
|
||||
:class="{
|
||||
disabled
|
||||
}"
|
||||
ref="Editor"
|
||||
:style="{ height: parsePx(height) }"
|
||||
></div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="cl-editor-monaco">
|
||||
import { onMounted, onUnmounted, ref, watch } from "vue";
|
||||
import * as Monaco from "monaco-editor/esm/vs/editor/editor.api";
|
||||
import "./worker";
|
||||
import "./theme";
|
||||
import { deepMerge, parsePx } from "/@/cool/utils";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: String,
|
||||
options: Object,
|
||||
height: {
|
||||
type: [String, Number],
|
||||
default: 400
|
||||
},
|
||||
autofocus: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
language: {
|
||||
type: String,
|
||||
default: "json"
|
||||
},
|
||||
disabled: Boolean
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue", "change"]);
|
||||
|
||||
let monaco: Monaco.editor.IStandaloneCodeEditor | null = null;
|
||||
|
||||
const Editor = ref();
|
||||
|
||||
function setContent(value?: string) {
|
||||
if (value != monaco?.getValue()) {
|
||||
monaco?.setValue(value || "");
|
||||
}
|
||||
}
|
||||
|
||||
function formatCode() {
|
||||
monaco?.getAction("editor.action.formatDocument").run();
|
||||
}
|
||||
|
||||
function init() {
|
||||
monaco = Monaco.editor.create(
|
||||
Editor.value,
|
||||
deepMerge(
|
||||
{
|
||||
language: props.language,
|
||||
minimap: {
|
||||
enabled: true
|
||||
},
|
||||
automaticLayout: true,
|
||||
scrollbar: {
|
||||
verticalScrollbarSize: 6
|
||||
},
|
||||
lineNumbersMinChars: 4,
|
||||
fontSize: 14,
|
||||
theme: "default",
|
||||
scrollBeyondLastLine: false,
|
||||
readOnly: props.disabled
|
||||
},
|
||||
props.options
|
||||
)
|
||||
);
|
||||
|
||||
monaco.onDidChangeModelContent(() => {
|
||||
const value = monaco?.getValue();
|
||||
|
||||
emit("update:modelValue", value);
|
||||
emit("change", value);
|
||||
});
|
||||
|
||||
setContent(props.modelValue);
|
||||
|
||||
if (props.autofocus) {
|
||||
setTimeout(() => {
|
||||
monaco?.focus();
|
||||
}, 10);
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.modelValue, setContent);
|
||||
watch(
|
||||
() => props.disabled,
|
||||
(val: boolean) => {
|
||||
monaco?.updateOptions({
|
||||
readOnly: val
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
init();
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
monaco?.dispose();
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
monaco,
|
||||
setContent,
|
||||
formatCode
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cl-editor-monaco {
|
||||
border: 1px solid var(--el-border-color);
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,350 @@
|
|||
import * as monaco from "monaco-editor/esm/vs/editor/editor.api";
|
||||
|
||||
monaco.editor.defineTheme("default", {
|
||||
base: "vs",
|
||||
inherit: true,
|
||||
rules: [
|
||||
{
|
||||
background: "ffffff",
|
||||
token: ""
|
||||
},
|
||||
{
|
||||
foreground: "6a737d",
|
||||
token: "comment"
|
||||
},
|
||||
{
|
||||
foreground: "6a737d",
|
||||
token: "punctuation.definition.comment"
|
||||
},
|
||||
{
|
||||
foreground: "6a737d",
|
||||
token: "string.comment"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "constant"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "entity.name.constant"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "variable.other.constant"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "variable.language"
|
||||
},
|
||||
{
|
||||
foreground: "6f42c1",
|
||||
token: "entity"
|
||||
},
|
||||
{
|
||||
foreground: "6f42c1",
|
||||
token: "entity.name"
|
||||
},
|
||||
{
|
||||
foreground: "24292e",
|
||||
token: "variable.parameter.function"
|
||||
},
|
||||
{
|
||||
foreground: "22863a",
|
||||
token: "entity.name.tag"
|
||||
},
|
||||
{
|
||||
foreground: "d73a49",
|
||||
token: "keyword"
|
||||
},
|
||||
{
|
||||
foreground: "d73a49",
|
||||
token: "storage"
|
||||
},
|
||||
{
|
||||
foreground: "d73a49",
|
||||
token: "storage.type"
|
||||
},
|
||||
{
|
||||
foreground: "24292e",
|
||||
token: "storage.modifier.package"
|
||||
},
|
||||
{
|
||||
foreground: "24292e",
|
||||
token: "storage.modifier.import"
|
||||
},
|
||||
{
|
||||
foreground: "24292e",
|
||||
token: "storage.type.java"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "string"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "punctuation.definition.string"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "string punctuation.section.embedded source"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "support"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "meta.property-name"
|
||||
},
|
||||
{
|
||||
foreground: "e36209",
|
||||
token: "variable"
|
||||
},
|
||||
{
|
||||
foreground: "24292e",
|
||||
token: "variable.other"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
fontStyle: "bold italic underline",
|
||||
token: "invalid.broken"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
fontStyle: "bold italic underline",
|
||||
token: "invalid.deprecated"
|
||||
},
|
||||
{
|
||||
foreground: "fafbfc",
|
||||
background: "b31d28",
|
||||
fontStyle: "italic underline",
|
||||
token: "invalid.illegal"
|
||||
},
|
||||
{
|
||||
foreground: "fafbfc",
|
||||
background: "d73a49",
|
||||
fontStyle: "italic underline",
|
||||
token: "carriage-return"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
fontStyle: "bold italic underline",
|
||||
token: "invalid.unimplemented"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
token: "message.error"
|
||||
},
|
||||
{
|
||||
foreground: "24292e",
|
||||
token: "string source"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "string variable"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "source.regexp"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "string.regexp"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "string.regexp.character-class"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "string.regexp constant.character.escape"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "string.regexp source.ruby.embedded"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
token: "string.regexp string.regexp.arbitrary-repitition"
|
||||
},
|
||||
{
|
||||
foreground: "22863a",
|
||||
fontStyle: "bold",
|
||||
token: "string.regexp constant.character.escape"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "support.constant"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "support.variable"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "meta.module-reference"
|
||||
},
|
||||
{
|
||||
foreground: "735c0f",
|
||||
token: "markup.list"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
fontStyle: "bold",
|
||||
token: "markup.heading"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
fontStyle: "bold",
|
||||
token: "markup.heading entity.name"
|
||||
},
|
||||
{
|
||||
foreground: "22863a",
|
||||
token: "markup.quote"
|
||||
},
|
||||
{
|
||||
foreground: "24292e",
|
||||
fontStyle: "italic",
|
||||
token: "markup.italic"
|
||||
},
|
||||
{
|
||||
foreground: "24292e",
|
||||
fontStyle: "bold",
|
||||
token: "markup.bold"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "markup.raw"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
background: "ffeef0",
|
||||
token: "markup.deleted"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
background: "ffeef0",
|
||||
token: "meta.diff.header.from-file"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
background: "ffeef0",
|
||||
token: "punctuation.definition.deleted"
|
||||
},
|
||||
{
|
||||
foreground: "22863a",
|
||||
background: "f0fff4",
|
||||
token: "markup.inserted"
|
||||
},
|
||||
{
|
||||
foreground: "22863a",
|
||||
background: "f0fff4",
|
||||
token: "meta.diff.header.to-file"
|
||||
},
|
||||
{
|
||||
foreground: "22863a",
|
||||
background: "f0fff4",
|
||||
token: "punctuation.definition.inserted"
|
||||
},
|
||||
{
|
||||
foreground: "e36209",
|
||||
background: "ffebda",
|
||||
token: "markup.changed"
|
||||
},
|
||||
{
|
||||
foreground: "e36209",
|
||||
background: "ffebda",
|
||||
token: "punctuation.definition.changed"
|
||||
},
|
||||
{
|
||||
foreground: "f6f8fa",
|
||||
background: "005cc5",
|
||||
token: "markup.ignored"
|
||||
},
|
||||
{
|
||||
foreground: "f6f8fa",
|
||||
background: "005cc5",
|
||||
token: "markup.untracked"
|
||||
},
|
||||
{
|
||||
foreground: "6f42c1",
|
||||
fontStyle: "bold",
|
||||
token: "meta.diff.range"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "meta.diff.header"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
fontStyle: "bold",
|
||||
token: "meta.separator"
|
||||
},
|
||||
{
|
||||
foreground: "005cc5",
|
||||
token: "meta.output"
|
||||
},
|
||||
{
|
||||
foreground: "586069",
|
||||
token: "brackethighlighter.tag"
|
||||
},
|
||||
{
|
||||
foreground: "586069",
|
||||
token: "brackethighlighter.curly"
|
||||
},
|
||||
{
|
||||
foreground: "586069",
|
||||
token: "brackethighlighter.round"
|
||||
},
|
||||
{
|
||||
foreground: "586069",
|
||||
token: "brackethighlighter.square"
|
||||
},
|
||||
{
|
||||
foreground: "586069",
|
||||
token: "brackethighlighter.angle"
|
||||
},
|
||||
{
|
||||
foreground: "586069",
|
||||
token: "brackethighlighter.quote"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
token: "brackethighlighter.unmatched"
|
||||
},
|
||||
{
|
||||
foreground: "b31d28",
|
||||
token: "sublimelinter.mark.error"
|
||||
},
|
||||
{
|
||||
foreground: "e36209",
|
||||
token: "sublimelinter.mark.warning"
|
||||
},
|
||||
{
|
||||
foreground: "959da5",
|
||||
token: "sublimelinter.gutter-mark"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
fontStyle: "underline",
|
||||
token: "constant.other.reference.link"
|
||||
},
|
||||
{
|
||||
foreground: "032f62",
|
||||
fontStyle: "underline",
|
||||
token: "string.other.link"
|
||||
}
|
||||
],
|
||||
colors: {
|
||||
"editor.foreground": "#24292e",
|
||||
"editor.background": "#ffffff",
|
||||
"editor.selectionBackground": "#c8c8fa",
|
||||
"editor.inactiveSelectionBackground": "#fafbfc",
|
||||
"editor.lineHighlightBackground": "#fafbfc",
|
||||
"editorCursor.foreground": "#24292e",
|
||||
"editorWhitespace.foreground": "#959da5",
|
||||
"editorIndentGuide.background": "#959da5",
|
||||
"editorIndentGuide.activeBackground": "#24292e",
|
||||
"editor.selectionHighlightBorder": "#fafbfc"
|
||||
}
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
import * as monaco from "monaco-editor";
|
||||
import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker";
|
||||
import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker";
|
||||
import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker";
|
||||
import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker";
|
||||
import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker";
|
||||
|
||||
self.MonacoEnvironment = {
|
||||
getWorker(_: any, label: string) {
|
||||
if (label === "json") {
|
||||
return new jsonWorker();
|
||||
}
|
||||
if (label === "css" || label === "scss" || label === "less") {
|
||||
return new cssWorker();
|
||||
}
|
||||
if (label === "html" || label === "handlebars" || label === "razor") {
|
||||
return new htmlWorker();
|
||||
}
|
||||
if (label === "typescript" || label === "javascript") {
|
||||
return new tsWorker();
|
||||
}
|
||||
return new editorWorker();
|
||||
}
|
||||
};
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setEagerModelSync(true);
|
||||
|
||||
monaco.languages.typescript.typescriptDefaults.setCompilerOptions({
|
||||
target: monaco.languages.typescript.ScriptTarget.ES2016,
|
||||
allowNonTsExtensions: true,
|
||||
moduleResolution: monaco.languages.typescript.ModuleResolutionKind.NodeJs,
|
||||
module: monaco.languages.typescript.ModuleKind.CommonJS,
|
||||
noEmit: true,
|
||||
allowJs: false
|
||||
});
|
|
@ -7,7 +7,7 @@
|
|||
ref="ImageSpace"
|
||||
accept="image/*"
|
||||
:show-btn="false"
|
||||
@confirm="onSpaceConfirm"
|
||||
@confirm="onFileConfirm"
|
||||
/>
|
||||
|
||||
<!-- 视频 -->
|
||||
|
@ -15,7 +15,7 @@
|
|||
ref="VideoSpace"
|
||||
accept="video/*"
|
||||
:show-btn="false"
|
||||
@confirm="onSpaceConfirm"
|
||||
@confirm="onFileConfirm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -24,7 +24,7 @@
|
|||
import { defineComponent, onMounted, ref, watch } from "vue";
|
||||
import Quill from "quill";
|
||||
import "quill/dist/quill.snow.css";
|
||||
import { useComm } from "/@/cool";
|
||||
import { parsePx } from "/@/cool/utils";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-editor-quill",
|
||||
|
@ -42,8 +42,6 @@ export default defineComponent({
|
|||
emits: ["update:modelValue", "load"],
|
||||
|
||||
setup(props, { emit }) {
|
||||
const { px } = useComm();
|
||||
|
||||
let quill: any = null;
|
||||
|
||||
// 编辑器
|
||||
|
@ -81,7 +79,7 @@ export default defineComponent({
|
|||
}
|
||||
|
||||
// 文件确认
|
||||
function onSpaceConfirm(files: any[]) {
|
||||
function onFileConfirm(files: any[]) {
|
||||
if (files.length > 0) {
|
||||
// 批量插入图片
|
||||
files.forEach((file, i) => {
|
||||
|
@ -107,7 +105,7 @@ export default defineComponent({
|
|||
|
||||
// 设置高度
|
||||
function setHeight() {
|
||||
quill.container.style.height = px(props.height);
|
||||
quill.container.style.height = parsePx(props.height);
|
||||
}
|
||||
|
||||
// 监听绑定值
|
||||
|
@ -187,7 +185,7 @@ export default defineComponent({
|
|||
quill,
|
||||
cursorIndex,
|
||||
setContent,
|
||||
onSpaceConfirm
|
||||
onFileConfirm
|
||||
};
|
||||
}
|
||||
});
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="cl-editor-wang" :class="{ disabled }">
|
||||
<!-- 工具栏 -->
|
||||
<toolbar :editor="editorRef" :mode="mode" />
|
||||
<toolbar :editor="Editor" :mode="mode" />
|
||||
|
||||
<!-- 编辑框 -->
|
||||
<editor
|
||||
|
@ -17,37 +17,29 @@
|
|||
|
||||
<!-- 图片 -->
|
||||
<cl-upload-space
|
||||
ref="ImageSpace"
|
||||
:ref="setRefs('image')"
|
||||
accept="image/*"
|
||||
:show-btn="false"
|
||||
@confirm="onSpaceConfirm"
|
||||
@confirm="onFileConfirm"
|
||||
/>
|
||||
|
||||
<!-- 视频 -->
|
||||
<cl-upload-space
|
||||
ref="VideoSpace"
|
||||
:ref="setRefs('video')"
|
||||
accept="video/*"
|
||||
:show-btn="false"
|
||||
@confirm="onSpaceConfirm"
|
||||
@confirm="onFileConfirm"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import "@wangeditor/editor/dist/css/style.css";
|
||||
import {
|
||||
onBeforeUnmount,
|
||||
ref,
|
||||
shallowRef,
|
||||
watch,
|
||||
PropType,
|
||||
reactive,
|
||||
computed,
|
||||
defineComponent
|
||||
} from "vue";
|
||||
import { onBeforeUnmount, ref, shallowRef, watch, PropType, computed, defineComponent } from "vue";
|
||||
import { Editor, Toolbar } from "@wangeditor/editor-for-vue";
|
||||
import { IEditorConfig } from "@wangeditor/editor";
|
||||
import { useComm } from "/@/cool";
|
||||
import { useCool } from "/@/cool";
|
||||
import { parsePx } from "/@/cool/utils";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-editor-wang",
|
||||
|
@ -73,16 +65,10 @@ export default defineComponent({
|
|||
emits: ["update:modelValue", "change", "focus", "blur"],
|
||||
|
||||
setup(props, { emit }) {
|
||||
const { px } = useComm();
|
||||
|
||||
// 图片上传
|
||||
const ImageSpace = ref();
|
||||
|
||||
// 视频上传
|
||||
const VideoSpace = ref();
|
||||
const { refs, setRefs } = useCool();
|
||||
|
||||
// 编辑器
|
||||
const editorRef = shallowRef();
|
||||
const Editor = shallowRef();
|
||||
|
||||
// 内容
|
||||
const value = ref();
|
||||
|
@ -90,7 +76,7 @@ export default defineComponent({
|
|||
// 编辑器样式
|
||||
const style = computed(() => {
|
||||
return {
|
||||
height: px(props.height)
|
||||
height: parsePx(props.height)
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -104,13 +90,39 @@ export default defineComponent({
|
|||
}
|
||||
);
|
||||
|
||||
function onCreated(editor: any) {
|
||||
editorRef.value = editor;
|
||||
const temp: { insertFn: ((url: string) => void) | null } = {
|
||||
insertFn: null
|
||||
};
|
||||
|
||||
// 配置
|
||||
const editorConfig: Partial<IEditorConfig> = {
|
||||
placeholder: "请输入",
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
customBrowseAndUpload(fn: any) {
|
||||
temp.insertFn = fn;
|
||||
refs.image.open();
|
||||
}
|
||||
},
|
||||
uploadVideo: {
|
||||
customBrowseAndUpload(fn: any) {
|
||||
temp.insertFn = fn;
|
||||
refs.video.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function onCreated(editor: any) {
|
||||
Editor.value = editor;
|
||||
onDisabled();
|
||||
}
|
||||
|
||||
function onDisabled() {
|
||||
if (props.disabled) {
|
||||
editor.disable();
|
||||
Editor.value?.disable();
|
||||
} else {
|
||||
editor.enable();
|
||||
Editor.value?.enable();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -127,48 +139,28 @@ export default defineComponent({
|
|||
emit("change", value.value);
|
||||
}
|
||||
|
||||
const temp = reactive<any>({
|
||||
insertFn: null
|
||||
});
|
||||
|
||||
// 配置
|
||||
const editorConfig: Partial<IEditorConfig> = {
|
||||
placeholder: "请输入",
|
||||
MENU_CONF: {
|
||||
uploadImage: {
|
||||
customBrowseAndUpload(insertFn: any) {
|
||||
temp.insertFn = insertFn;
|
||||
ImageSpace.value.open();
|
||||
}
|
||||
},
|
||||
uploadVideo: {
|
||||
customBrowseAndUpload(insertFn: any) {
|
||||
temp.insertFn = insertFn;
|
||||
VideoSpace.value.open();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 文件确认
|
||||
function onSpaceConfirm(files: any[]) {
|
||||
function onFileConfirm(files: any[]) {
|
||||
if (files.length > 0) {
|
||||
files.forEach((file) => {
|
||||
temp.insertFn(file.url);
|
||||
if (temp.insertFn) {
|
||||
temp.insertFn(file.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const editor = editorRef.value;
|
||||
const editor = Editor.value;
|
||||
if (editor == null) return;
|
||||
editor.destroy();
|
||||
});
|
||||
|
||||
watch(() => props.disabled, onDisabled);
|
||||
|
||||
return {
|
||||
ImageSpace,
|
||||
VideoSpace,
|
||||
editorRef,
|
||||
refs,
|
||||
setRefs,
|
||||
Editor,
|
||||
value,
|
||||
style,
|
||||
onCreated,
|
||||
|
@ -176,7 +168,7 @@ export default defineComponent({
|
|||
onBlur,
|
||||
onChange,
|
||||
editorConfig,
|
||||
onSpaceConfirm
|
||||
onFileConfirm
|
||||
};
|
||||
}
|
||||
});
|
||||
|
@ -198,5 +190,9 @@ export default defineComponent({
|
|||
background-color: var(--el-disabled-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
&.w-e-full-screen-container {
|
||||
z-index: 999;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,12 @@
|
|||
import { ModuleConfig } from "/@/cool";
|
||||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
// 根据使用情况选择组件,避免资源过大问题
|
||||
components: [
|
||||
() => import("./components/wang.vue"),
|
||||
() => import("./components/quill.vue"),
|
||||
() => import("./components/monaco/index.vue")
|
||||
]
|
||||
};
|
||||
};
|
|
@ -0,0 +1,158 @@
|
|||
import { defineComponent, PropType } from "vue";
|
||||
import { useCrud } from "@cool-vue/crud";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { isRef, ref } from "vue";
|
||||
import { currentDate, export_json_to_excel } from "../utils";
|
||||
|
||||
export default defineComponent({
|
||||
name: "cl-export-btn",
|
||||
|
||||
props: {
|
||||
filename: [Function, String],
|
||||
autoWidth: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
bookType: {
|
||||
type: String,
|
||||
default: "xlsx"
|
||||
},
|
||||
header: Array,
|
||||
columns: {
|
||||
type: Array as PropType<any[]>,
|
||||
required: true
|
||||
},
|
||||
data: [Function, Array],
|
||||
maxExportLimit: Number // 最大导出条数,不传或者小于等于0为不限制
|
||||
},
|
||||
|
||||
setup(props) {
|
||||
// 加载状态
|
||||
const loading = ref(false);
|
||||
|
||||
// crud
|
||||
const Crud = useCrud();
|
||||
|
||||
// 获取表头数据
|
||||
async function getHeader(columns: any[], fields: any[]) {
|
||||
return columns.filter((e) => !e.hidden && fields.includes(e.prop)).map((e) => e.label);
|
||||
}
|
||||
|
||||
// 获取表格数据
|
||||
function getData() {
|
||||
if (typeof props.data === "function") {
|
||||
return props.data();
|
||||
} else {
|
||||
if (props.data) {
|
||||
return props.data;
|
||||
} else {
|
||||
return Crud.value?.service
|
||||
.page({
|
||||
...Crud.value?.paramsReplace(Crud.value.params),
|
||||
maxExportLimit: props.maxExportLimit,
|
||||
isExport: true
|
||||
})
|
||||
.then((res) => {
|
||||
return res.list.map((e) => {
|
||||
for (const i in e) {
|
||||
const col = props.columns.find((c) => c.prop == i);
|
||||
|
||||
if (col) {
|
||||
if (col.formatter) {
|
||||
e = col.formatter(e);
|
||||
}
|
||||
|
||||
if (col.dict) {
|
||||
const d = (
|
||||
isRef(col.dict) ? col.dict.value : col.dict
|
||||
).find((d: any) => d.value == e[i]);
|
||||
|
||||
if (d) {
|
||||
e[i] = d.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return e;
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 获取文件名
|
||||
async function getFileName() {
|
||||
if (typeof props.filename === "function") {
|
||||
return await props?.filename();
|
||||
} else {
|
||||
const { year, month, day, hour, minu, sec } = currentDate();
|
||||
return props.filename || `报表(${year}-${month}-${day} ${hour}_${minu}_${sec})`;
|
||||
}
|
||||
}
|
||||
|
||||
// 导出
|
||||
async function toExport() {
|
||||
if (!props.columns) {
|
||||
return console.error("columns is required");
|
||||
}
|
||||
|
||||
// 加载
|
||||
loading.value = true;
|
||||
|
||||
// 表格列
|
||||
const columns = props.columns.filter(
|
||||
(e: any) =>
|
||||
!(
|
||||
e.hidden === true ||
|
||||
["selection", "expand", "index"].includes(e.type) ||
|
||||
e.filterExport ||
|
||||
e["filter-export"]
|
||||
)
|
||||
);
|
||||
|
||||
// 字段
|
||||
const fields = columns.map((e: any) => e.prop).filter(Boolean);
|
||||
|
||||
// 表头
|
||||
const header = await getHeader(columns, fields);
|
||||
|
||||
// 数据
|
||||
let data = await getData();
|
||||
|
||||
if (!data) {
|
||||
loading.value = false;
|
||||
return console.error("导出数据异常");
|
||||
}
|
||||
|
||||
// 文件名
|
||||
const filename = await getFileName();
|
||||
|
||||
// 过滤
|
||||
data = data.map((d: any) => fields.map((f) => d[f]));
|
||||
|
||||
// 导出 excel
|
||||
export_json_to_excel({
|
||||
header,
|
||||
data,
|
||||
filename,
|
||||
autoWidth: props.autoWidth,
|
||||
bookType: props.bookType
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
return () => {
|
||||
return (
|
||||
<el-button loading={loading.value} onClick={toExport}>
|
||||
<slot>导出</slot>
|
||||
</el-button>
|
||||
);
|
||||
};
|
||||
}
|
||||
});
|
|
@ -1,158 +0,0 @@
|
|||
<template>
|
||||
<el-button
|
||||
:size="size"
|
||||
:type="type"
|
||||
:plain="plain"
|
||||
:round="round"
|
||||
:circle="circle"
|
||||
:icon="icon"
|
||||
:loading="loading"
|
||||
:disabled="disabled"
|
||||
@click="toExport"
|
||||
>
|
||||
<slot>导出</slot>
|
||||
</el-button>
|
||||
</template>
|
||||
|
||||
<script lang="ts" name="cl-export-btn" setup>
|
||||
import { useCrud } from "@cool-vue/crud";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ref } from "vue";
|
||||
import { currentDate, export_json_to_excel } from "../utils";
|
||||
|
||||
const props = defineProps({
|
||||
filename: [Function, String],
|
||||
autoWidth: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
bookType: {
|
||||
type: String,
|
||||
default: "xlsx"
|
||||
},
|
||||
header: Array,
|
||||
columns: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
data: [Function, Array],
|
||||
maxExportLimit: Number, // 最大导出条数,不传或者小于等于0为不限制
|
||||
size: String,
|
||||
disabled: Boolean,
|
||||
type: String,
|
||||
plain: Boolean,
|
||||
round: Boolean,
|
||||
circle: Boolean,
|
||||
icon: String
|
||||
});
|
||||
|
||||
// 加载状态
|
||||
const loading = ref<boolean>(false);
|
||||
|
||||
// crud
|
||||
const Crud = useCrud();
|
||||
|
||||
async function getHeader(columns: any[], fields: any[]) {
|
||||
return columns.filter((e) => !e.hidden && fields.includes(e.prop)).map((e) => e.label);
|
||||
}
|
||||
|
||||
function getData() {
|
||||
if (typeof props.data === "function") {
|
||||
return props.data();
|
||||
} else {
|
||||
if (props.data) {
|
||||
return props.data;
|
||||
} else {
|
||||
return Crud.value?.service
|
||||
.page({
|
||||
...Crud.value?.paramsReplace(Crud.value.params),
|
||||
maxExportLimit: props.maxExportLimit,
|
||||
isExport: true
|
||||
})
|
||||
.then((res) => {
|
||||
return res.list.map((e: any) => {
|
||||
for (const i in e) {
|
||||
const col: any = props.columns.find((c: any) => c.prop == i);
|
||||
|
||||
if (col) {
|
||||
if (col.dict) {
|
||||
const d = col.dict.find((d: any) => d.value == e[i]);
|
||||
|
||||
if (d) {
|
||||
e[i] = d.label;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return e;
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function getFileName() {
|
||||
if (typeof props.filename === "function") {
|
||||
return await props?.filename();
|
||||
} else {
|
||||
const { year, month, day, hour, minu, sec } = currentDate();
|
||||
return props.filename || `报表(${year}-${month}-${day} ${hour}_${minu}_${sec})`;
|
||||
}
|
||||
}
|
||||
|
||||
async function toExport() {
|
||||
if (!props.columns) {
|
||||
return console.error("cl-export-btn.columns 不能为空!");
|
||||
}
|
||||
|
||||
// 加载
|
||||
loading.value = true;
|
||||
|
||||
// 表格列
|
||||
const columns = props.columns.filter(
|
||||
(e: any) =>
|
||||
!(
|
||||
e.hidden === true ||
|
||||
["selection", "expand", "index"].includes(e.type) ||
|
||||
e.filterExport ||
|
||||
e["filter-export"]
|
||||
)
|
||||
);
|
||||
|
||||
// 字段
|
||||
const fields = columns.map((e: any) => e.prop).filter(Boolean);
|
||||
|
||||
// 表头
|
||||
let header = await getHeader(columns, fields);
|
||||
|
||||
// 数据
|
||||
let data = await getData();
|
||||
|
||||
if (!data) {
|
||||
loading.value = false;
|
||||
return console.error("导出数据异常");
|
||||
}
|
||||
|
||||
// 文件名
|
||||
let filename = await getFileName();
|
||||
|
||||
// 过滤
|
||||
data = data.map((d: any) => fields.map((f) => d[f]));
|
||||
|
||||
// 导出 excel
|
||||
export_json_to_excel({
|
||||
header,
|
||||
data,
|
||||
filename,
|
||||
autoWidth: props.autoWidth,
|
||||
bookType: props.bookType
|
||||
});
|
||||
|
||||
loading.value = false;
|
||||
}
|
||||
</script>
|
|
@ -2,6 +2,6 @@ import { ModuleConfig } from "/@/cool";
|
|||
|
||||
export default (): ModuleConfig => {
|
||||
return {
|
||||
components: [import("./components/export-btn.vue")]
|
||||
components: [import("./components/export-btn")]
|
||||
};
|
||||
};
|
||||
|
|
|
@ -364,7 +364,7 @@ function refreshTask(params?: any, options?: any) {
|
|||
moreList(res, item);
|
||||
|
||||
if (!more) {
|
||||
refs.value[`${item.key}-scroller`].scroll({
|
||||
refs[`${item.key}-scroller`].scroll({
|
||||
top: 0,
|
||||
behavior: "smooth"
|
||||
});
|
||||
|
@ -652,7 +652,7 @@ async function refreshLog(newParams: any, options?: any) {
|
|||
moreList(res, logs);
|
||||
|
||||
if (!more) {
|
||||
refs.value["log-scroller"].scroll({
|
||||
refs["log-scroller"].scroll({
|
||||
top: 0,
|
||||
behavior: "smooth"
|
||||
});
|
||||
|
|
|
@ -1,33 +1,27 @@
|
|||
<template>
|
||||
<div
|
||||
class="cl-upload-space-category"
|
||||
:class="{
|
||||
'is-position': app.browser.isMini,
|
||||
'is-show': space.category.visible
|
||||
}"
|
||||
>
|
||||
<div class="cl-upload-space-category__search">
|
||||
<el-input v-model="keyword" placeholder="搜索分类" clearable />
|
||||
<el-button type="success" @click="edit()">添加</el-button>
|
||||
<div class="item-category">
|
||||
<div class="item-category__head">
|
||||
<span>类型</span>
|
||||
<el-button type="success" bg size="small" @click="edit()">添加</el-button>
|
||||
</div>
|
||||
|
||||
<div class="cl-upload-space-category__list">
|
||||
<div class="item-category__list">
|
||||
<el-scrollbar>
|
||||
<ul>
|
||||
<li
|
||||
v-for="(item, index) in flist"
|
||||
:key="index"
|
||||
class="item"
|
||||
:class="{
|
||||
'is-active': item.id == space.category.id
|
||||
'is-active': item.id == ViewGroup?.selected?.id
|
||||
}"
|
||||
@click="select(item.id)"
|
||||
@click="select(item)"
|
||||
@contextmenu.stop.prevent="onContextMenu($event, item)"
|
||||
>
|
||||
<el-icon class="icon">
|
||||
<folder-opened v-if="ViewGroup?.selected?.id == item.id" />
|
||||
<folder v-else />
|
||||
</el-icon>
|
||||
<span>{{ item.name }}</span>
|
||||
<el-icon v-show="space.category.id == item.id"
|
||||
><arrow-right-bold
|
||||
/></el-icon>
|
||||
</li>
|
||||
|
||||
<el-empty v-if="flist.length == 0" :image-size="80" />
|
||||
|
@ -39,19 +33,19 @@
|
|||
<cl-form ref="Form" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="space-category">
|
||||
<script lang="ts" setup name="item-category">
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { ArrowRightBold } from "@element-plus/icons-vue";
|
||||
import { Folder, FolderOpened } from "@element-plus/icons-vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { isEmpty } from "lodash-es";
|
||||
import { useCool } from "/@/cool";
|
||||
import { ContextMenu, useForm } from "@cool-vue/crud";
|
||||
import { useBase } from "/$/base";
|
||||
import { useViewGroup } from "/$/base";
|
||||
import { useSpace } from "../../hooks";
|
||||
|
||||
const { service } = useCool();
|
||||
const { app } = useBase();
|
||||
const { space } = useSpace();
|
||||
const { ViewGroup } = useViewGroup();
|
||||
const Form = useForm();
|
||||
|
||||
// 数据列表
|
||||
const list = ref<Eps.SpaceTypeEntity[]>([]);
|
||||
|
@ -66,29 +60,36 @@ const flist = computed(() => {
|
|||
|
||||
// 刷新分类
|
||||
async function refresh() {
|
||||
return service.space.type.list().then((res) => {
|
||||
res.unshift({
|
||||
name: "全部文件",
|
||||
id: undefined
|
||||
});
|
||||
return service.space.type
|
||||
.list({
|
||||
order: "createTime",
|
||||
sort: "asc"
|
||||
})
|
||||
.then((res) => {
|
||||
res.unshift({
|
||||
name: "全部文件",
|
||||
id: undefined
|
||||
});
|
||||
|
||||
list.value = res;
|
||||
list.value = res;
|
||||
|
||||
if (!isEmpty(res)) {
|
||||
if (!space.category.id && res[0].id) {
|
||||
space.category.id = res[0].id;
|
||||
if (!ViewGroup.value?.selected) {
|
||||
select();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const Form = useForm();
|
||||
|
||||
// 编辑分类
|
||||
function edit(item: Eps.SpaceTypeEntity = {}) {
|
||||
Form.value?.open({
|
||||
title: "添加分类",
|
||||
width: "400px",
|
||||
props: {
|
||||
labelPosition: "top"
|
||||
},
|
||||
dialog: {
|
||||
controls: ["close"]
|
||||
},
|
||||
items: [
|
||||
{
|
||||
label: "分类名称",
|
||||
|
@ -96,7 +97,11 @@ function edit(item: Eps.SpaceTypeEntity = {}) {
|
|||
value: "",
|
||||
required: true,
|
||||
component: {
|
||||
name: "el-input"
|
||||
name: "el-input",
|
||||
props: {
|
||||
maxlength: 20,
|
||||
clearable: true
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
@ -129,18 +134,21 @@ function edit(item: Eps.SpaceTypeEntity = {}) {
|
|||
}
|
||||
|
||||
// 选择类目
|
||||
function select(id?: number) {
|
||||
// 小屏幕下收起左侧类目
|
||||
if (app.browser.isMini) {
|
||||
space.category.visible = false;
|
||||
function select(item?: Eps.SpaceTypeEntity) {
|
||||
if (!item) {
|
||||
item = list.value[0];
|
||||
}
|
||||
|
||||
space.category.id = id;
|
||||
space.refresh({ page: 1 });
|
||||
if (item) {
|
||||
space.refresh({ page: 1, classifyId: item.id });
|
||||
|
||||
ViewGroup.value?.select(item);
|
||||
ViewGroup.value?.setTitle(item.name);
|
||||
}
|
||||
}
|
||||
|
||||
// 打开类目列表右键菜单
|
||||
function onContextMenu(e: any, { id, name }: any) {
|
||||
function onContextMenu(e: any, { id, name }: Eps.SpaceTypeEntity) {
|
||||
if (!id) {
|
||||
return false;
|
||||
}
|
||||
|
@ -179,8 +187,8 @@ function onContextMenu(e: any, { id, name }: any) {
|
|||
ElMessage.success("删除成功");
|
||||
|
||||
// 是否删除当前
|
||||
if (id == space.category.id) {
|
||||
space.category.id = undefined;
|
||||
if (id == ViewGroup.value?.selected?.id) {
|
||||
select();
|
||||
}
|
||||
|
||||
refresh();
|
||||
|
@ -204,49 +212,29 @@ onMounted(() => {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cl-upload-space-category {
|
||||
.item-category {
|
||||
height: 100%;
|
||||
width: 0;
|
||||
background-color: var(--el-bg-color);
|
||||
overflow: hidden;
|
||||
transition: width 0.2s ease-in-out;
|
||||
border-radius: 5px;
|
||||
width: 100%;
|
||||
|
||||
&.is-show {
|
||||
width: 220px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
&.is-position {
|
||||
position: absolute;
|
||||
left: 5px;
|
||||
top: 51px;
|
||||
height: calc(100% - 56px);
|
||||
z-index: 3000;
|
||||
|
||||
&.is-show {
|
||||
width: calc(100% - 10px);
|
||||
}
|
||||
}
|
||||
|
||||
&__search {
|
||||
&__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px;
|
||||
|
||||
.el-button {
|
||||
margin-left: 10px;
|
||||
}
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
font-size: 14px;
|
||||
padding: 0 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&__list {
|
||||
height: calc(100% - 48px);
|
||||
height: calc(100% - 40px);
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
|
||||
ul {
|
||||
height: 100%;
|
||||
|
||||
.item {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
list-style: none;
|
||||
|
@ -260,7 +248,12 @@ onMounted(() => {
|
|||
color: #666;
|
||||
position: relative;
|
||||
|
||||
.el-icon {
|
||||
.icon {
|
||||
margin-right: 10px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.arrow {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
}
|
||||
|
@ -274,6 +267,12 @@ onMounted(() => {
|
|||
background-color: #f7f7f7;
|
||||
}
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
height: 1px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,14 @@
|
|||
<template>
|
||||
<div class="cl-upload-space-file__wrap">
|
||||
<div class="item-file__wrap">
|
||||
<div
|
||||
class="cl-upload-space-file"
|
||||
class="item-file"
|
||||
:class="[`is-${info.type}`]"
|
||||
@click="select"
|
||||
@contextmenu.stop.prevent="onContextMenu"
|
||||
>
|
||||
<!-- 错误 -->
|
||||
<template v-if="info.error">
|
||||
<div class="cl-upload-space-file__error">上传失败:{{ info.error }}</div>
|
||||
<div class="item-file__error">上传失败:{{ info.error }}</div>
|
||||
</template>
|
||||
|
||||
<!-- 成功 -->
|
||||
|
@ -32,14 +32,12 @@
|
|||
<!-- 其他 -->
|
||||
<template v-else>
|
||||
<!-- 文件名 -->
|
||||
<span class="cl-upload-space-file__name"
|
||||
>{{ fileName(url) }}.{{ extname(url) }}</span
|
||||
>
|
||||
<span class="item-file__name">{{ fileName(url) }}.{{ extname(url) }}</span>
|
||||
</template>
|
||||
|
||||
<!-- 文件类型 -->
|
||||
<span
|
||||
class="cl-upload-space-file__type"
|
||||
class="item-file__type"
|
||||
:style="{
|
||||
backgroundColor: type?.color
|
||||
}"
|
||||
|
@ -49,44 +47,48 @@
|
|||
<!-- 上传中 -->
|
||||
<template v-if="info.progress > 0 && info.progress < 100">
|
||||
<!-- 进度条 -->
|
||||
<div class="cl-upload-space-file__progress-bar">
|
||||
<div class="item-file__progress-bar">
|
||||
<el-progress :percentage="info.progress" :show-text="false"></el-progress>
|
||||
</div>
|
||||
|
||||
<!-- 进度值 -->
|
||||
<span class="cl-upload-space-file__progress-value">{{ info.progress }}</span>
|
||||
<span class="item-file__progress-value">{{ info.progress }}</span>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 遮罩层 -->
|
||||
<div v-if="isSelected" class="cl-upload-space-file__mask">
|
||||
<div v-if="isSelected" class="item-file__mask">
|
||||
<span>{{ index + 1 }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<item-viewer ref="Viewer" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="space-file">
|
||||
import { computed } from "vue";
|
||||
<script lang="ts" setup name="item-file">
|
||||
import { computed, PropType, ref } from "vue";
|
||||
import { ContextMenu } from "@cool-vue/crud";
|
||||
import { extname } from "/@/cool/utils";
|
||||
import { fileName, fileRule } from "../../utils";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useSpace } from "../../hooks";
|
||||
import ItemVideo from "./item-video.vue";
|
||||
|
||||
const { copy } = useClipboard();
|
||||
import ItemVideo from "./video.vue";
|
||||
import ItemViewer from "./viewer.vue";
|
||||
|
||||
const props = defineProps({
|
||||
data: Object,
|
||||
list: Array
|
||||
list: Array as PropType<Eps.SpaceInfoEntity[]>
|
||||
});
|
||||
|
||||
const emit = defineEmits(["select", "remove"]);
|
||||
|
||||
const { copy } = useClipboard();
|
||||
const { space } = useSpace();
|
||||
|
||||
const Viewer = ref();
|
||||
|
||||
// 文件信息
|
||||
const info = computed<Eps.SpaceInfoEntity>(() => props.data || {});
|
||||
|
||||
|
@ -100,7 +102,7 @@ const isSelected = computed(() => index.value >= 0);
|
|||
const url = computed(() => info.value.preload || info.value.url);
|
||||
|
||||
// 类型
|
||||
const type = computed(() => fileRule(info.value.type));
|
||||
const type = computed(() => fileRule(info.value.type || ""));
|
||||
|
||||
// 选择
|
||||
function select() {
|
||||
|
@ -116,13 +118,21 @@ function remove() {
|
|||
function onContextMenu(e: any) {
|
||||
ContextMenu.open(e, {
|
||||
hover: {
|
||||
target: "cl-upload-space-file__wrap"
|
||||
target: "item-file__wrap"
|
||||
},
|
||||
list: [
|
||||
{
|
||||
label: "预览",
|
||||
callback(done) {
|
||||
window.open(info.value.url);
|
||||
if (info.value.type == "image") {
|
||||
Viewer.value?.open(
|
||||
info.value.url,
|
||||
props.list?.filter((e) => e.type == "image").map((e) => e.url)
|
||||
);
|
||||
} else {
|
||||
window.open(info.value.url);
|
||||
}
|
||||
|
||||
done();
|
||||
}
|
||||
},
|
||||
|
@ -157,7 +167,7 @@ function onContextMenu(e: any) {
|
|||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.cl-upload-space-file {
|
||||
.item-file {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
@ -165,7 +175,6 @@ function onContextMenu(e: any) {
|
|||
height: 100%;
|
||||
width: 100%;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
|
@ -173,6 +182,9 @@ function onContextMenu(e: any) {
|
|||
margin-bottom: 10px;
|
||||
|
||||
&__wrap {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
@ -207,7 +219,7 @@ function onContextMenu(e: any) {
|
|||
&:not(.is-image):not(.is-video) {
|
||||
padding: 10px;
|
||||
|
||||
.cl-upload-space-file {
|
||||
.item-file {
|
||||
&__icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -294,6 +306,7 @@ function onContextMenu(e: any) {
|
|||
text-align: center;
|
||||
line-height: 20px;
|
||||
border-radius: 20px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,10 +26,10 @@ const props = defineProps({
|
|||
|
||||
const { space } = useSpace();
|
||||
|
||||
const info = computed<Eps.SpaceInfoEntity>(() => props.data || {});
|
||||
|
||||
const Video = ref<HTMLVideoElement>();
|
||||
|
||||
const info = computed<Eps.SpaceInfoEntity>(() => props.data || {});
|
||||
|
||||
const loaded = computed(() => {
|
||||
return info.value.progress === undefined || info.value.progress === 100;
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
<template>
|
||||
<div class="item-viewer">
|
||||
<el-image-viewer
|
||||
v-if="visible"
|
||||
:url-list="urls"
|
||||
:initial-index="index"
|
||||
infinite
|
||||
teleported
|
||||
@close="close"
|
||||
></el-image-viewer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="item-viewer">
|
||||
import { ref } from "vue";
|
||||
|
||||
const visible = ref(false);
|
||||
const urls = ref<string[]>([]);
|
||||
const index = ref(0);
|
||||
|
||||
function open(url: string, list: string[]) {
|
||||
visible.value = true;
|
||||
|
||||
urls.value = list;
|
||||
index.value = list.findIndex((e) => e == url);
|
||||
}
|
||||
|
||||
function close() {
|
||||
visible.value = false;
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,371 @@
|
|||
<template>
|
||||
<div class="cl-upload-panel" @dragover="onDragover" @drop="onDrop">
|
||||
<cl-view-group ref="ViewGroup" title="全部文件">
|
||||
<template #left>
|
||||
<item-category />
|
||||
</template>
|
||||
|
||||
<template #right>
|
||||
<div class="cl-upload-panel__right" ref="Content">
|
||||
<!-- 操作栏 -->
|
||||
<div class="cl-upload-panel__header">
|
||||
<el-button @click="refresh({ page: 1 })">刷新</el-button>
|
||||
|
||||
<div :style="{ margin: '0px 10px' }">
|
||||
<cl-upload
|
||||
ref="Upload"
|
||||
type="file"
|
||||
:show-file-list="false"
|
||||
:limit="limit"
|
||||
:limit-upload="false"
|
||||
:accept="accept"
|
||||
multiple
|
||||
@success="onSuccess"
|
||||
@upload="onUpload"
|
||||
>
|
||||
<el-button type="primary">点击上传</el-button>
|
||||
</cl-upload>
|
||||
</div>
|
||||
|
||||
<template v-if="!selectable">
|
||||
<el-button
|
||||
type="danger"
|
||||
:disabled="selection.length == 0"
|
||||
@click="remove()"
|
||||
>删除选中文件</el-button
|
||||
>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- 文件区域 -->
|
||||
<el-scrollbar class="cl-upload-panel__file" v-loading="loading">
|
||||
<div v-infinite-scroll="loadmore" :infinite-scroll-immediate="false">
|
||||
<!-- 文件列表 -->
|
||||
<template v-if="list.length > 0">
|
||||
<div
|
||||
class="list"
|
||||
:class="{
|
||||
'is-mini': browser.isMini
|
||||
}"
|
||||
>
|
||||
<div
|
||||
class="item"
|
||||
v-for="item in list"
|
||||
:key="item.preload || item.url"
|
||||
>
|
||||
<item-file
|
||||
:data="item"
|
||||
:list="list"
|
||||
@select="select"
|
||||
@remove="remove"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 空态 -->
|
||||
<div v-else class="empty">
|
||||
<el-icon class="el-icon--upload">
|
||||
<upload-filled />
|
||||
</el-icon>
|
||||
<p>将文件拖到此处,或点击按钮上传</p>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
</cl-view-group>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="cl-upload-panel">
|
||||
import { provide, reactive, ref, watch } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { UploadFilled } from "@element-plus/icons-vue";
|
||||
import { useCool } from "/@/cool";
|
||||
import { useViewGroup } from "/$/base";
|
||||
import ItemCategory from "./items/category.vue";
|
||||
import ItemFile from "./items/file.vue";
|
||||
|
||||
const props = defineProps({
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 99
|
||||
},
|
||||
accept: String,
|
||||
selectable: Boolean
|
||||
});
|
||||
|
||||
const emit = defineEmits(["selection-change"]);
|
||||
|
||||
const { service, browser } = useCool();
|
||||
const { ViewGroup } = useViewGroup();
|
||||
|
||||
// cl-upload
|
||||
const Upload = ref();
|
||||
|
||||
// 右侧内容
|
||||
const Content = ref();
|
||||
|
||||
// 是否加载中
|
||||
const loading = ref(false);
|
||||
|
||||
// 已选列表
|
||||
const selection = ref<Eps.SpaceInfoEntity[]>([]);
|
||||
|
||||
// 文件列表
|
||||
const list = ref<Eps.SpaceInfoEntity[]>([]);
|
||||
|
||||
// 分页信息
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
size: 50,
|
||||
total: 0
|
||||
});
|
||||
|
||||
// 清空选择
|
||||
function clear() {
|
||||
selection.value = [];
|
||||
}
|
||||
|
||||
// 上传成功
|
||||
function onSuccess(data: any) {
|
||||
service.space.info
|
||||
.add({
|
||||
classifyId: ViewGroup.value?.selected?.id,
|
||||
...data
|
||||
})
|
||||
.then((res) => {
|
||||
data.id = res.id;
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
// 上传时
|
||||
function onUpload(data: any) {
|
||||
list.value.unshift(data);
|
||||
}
|
||||
|
||||
// 请求参数
|
||||
const reqParams = {
|
||||
page: 1
|
||||
};
|
||||
|
||||
// 刷新列表
|
||||
async function refresh(params?: any) {
|
||||
// 清空选择
|
||||
clear();
|
||||
|
||||
// 合并参数
|
||||
Object.assign(reqParams, {
|
||||
type: props.accept?.split("/")[0],
|
||||
...pagination,
|
||||
...params
|
||||
});
|
||||
|
||||
// 加载中
|
||||
if (reqParams.page == 1) {
|
||||
loading.value = true;
|
||||
}
|
||||
|
||||
await service.space.info.page(reqParams).then((res) => {
|
||||
// 合并分页
|
||||
Object.assign(pagination, res.pagination);
|
||||
|
||||
if (reqParams.page == 1) {
|
||||
list.value = [];
|
||||
}
|
||||
|
||||
list.value.push(...res.list);
|
||||
});
|
||||
|
||||
// 加载完成
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 选择
|
||||
function select(item: Eps.SpaceInfoEntity) {
|
||||
const index = selection.value.findIndex((e) => e.id === item.id);
|
||||
|
||||
if (index >= 0) {
|
||||
selection.value.splice(index, 1);
|
||||
} else {
|
||||
if (props.limit == 1) {
|
||||
selection.value = [item];
|
||||
} else {
|
||||
if (selection.value.length < props.limit) {
|
||||
selection.value.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除选中
|
||||
function remove(item?: Eps.SpaceInfoEntity) {
|
||||
// 已选文件 id
|
||||
const ids = item ? [item.id] : selection.value.map((e) => e.id);
|
||||
|
||||
ElMessageBox.confirm("此操作将删除文件, 是否继续?", "提示", {
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
|
||||
// 删除文件及选择
|
||||
ids.forEach((id) => {
|
||||
[list.value, selection.value].forEach((list) => {
|
||||
const index = list.findIndex((e) => e.id === id);
|
||||
list.splice(index, 1);
|
||||
});
|
||||
});
|
||||
|
||||
// 删除请求
|
||||
service.space.info
|
||||
.delete({
|
||||
ids
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
})
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
// 监听选择
|
||||
watch(
|
||||
selection,
|
||||
(val) => {
|
||||
emit("selection-change", val);
|
||||
},
|
||||
{
|
||||
deep: true
|
||||
}
|
||||
);
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (list.value.length && list.value.length < pagination.total) {
|
||||
refresh({
|
||||
page: pagination.page + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function onDragover(e: any) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function onDrop(e: any) {
|
||||
e.preventDefault();
|
||||
|
||||
e.dataTransfer.files.forEach((f: File, i: number) => {
|
||||
setTimeout(() => {
|
||||
Upload.value.upload(f);
|
||||
}, i * 10);
|
||||
});
|
||||
}
|
||||
|
||||
provide("space", {
|
||||
selection,
|
||||
refresh,
|
||||
loading,
|
||||
list
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
selection,
|
||||
open,
|
||||
close,
|
||||
clear,
|
||||
refresh
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.cl-upload-panel {
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
|
||||
&__right {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&__file {
|
||||
height: calc(100% - 50px);
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
height: 0;
|
||||
min-height: 140px;
|
||||
min-width: 140px;
|
||||
width: calc(12.5% - 10px);
|
||||
padding-top: calc(12.5% - 10px);
|
||||
margin: 0 10px 10px 0;
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&.is-mini {
|
||||
.item {
|
||||
width: calc(50% - 10px);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: auto;
|
||||
height: 180px;
|
||||
width: 360px;
|
||||
max-width: 80%;
|
||||
border-radius: 5px;
|
||||
border: 1px dashed var(--el-border-color);
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 67px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin-top: 15px;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
padding: 9px 0;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -9,451 +9,87 @@
|
|||
v-model="visible"
|
||||
:title="title"
|
||||
height="650px"
|
||||
width="1080px"
|
||||
width="1070px"
|
||||
keep-alive
|
||||
custom-class="cl-upload-space__dialog"
|
||||
:close-on-click-modal="false"
|
||||
append-to-body
|
||||
:controls="['slot-expand', 'cl-flex1', 'fullscreen', 'close']"
|
||||
>
|
||||
<div
|
||||
class="cl-upload-space"
|
||||
:class="{
|
||||
'is-mini': app.browser.isMini
|
||||
}"
|
||||
@dragover="onDragover"
|
||||
@drop="onDrop"
|
||||
>
|
||||
<!-- 类目 -->
|
||||
<space-category />
|
||||
<cl-upload-panel :limit="limit" :accept="accept" ref="Panel" />
|
||||
|
||||
<!-- 内容 -->
|
||||
<div class="cl-upload-space__content">
|
||||
<!-- 操作栏 -->
|
||||
<div class="cl-upload-space__header scroller1">
|
||||
<el-button @click="refresh({ page: 1 })">刷新</el-button>
|
||||
|
||||
<div :style="{ margin: '0px 10px' }">
|
||||
<cl-upload
|
||||
ref="Upload"
|
||||
type="file"
|
||||
:show-file-list="false"
|
||||
:disabled="disabled"
|
||||
:limit="9999"
|
||||
:accept="accept"
|
||||
multiple
|
||||
@success="onSuccess"
|
||||
@upload="onUpload"
|
||||
>
|
||||
<el-button type="primary">点击上传</el-button>
|
||||
</cl-upload>
|
||||
</div>
|
||||
|
||||
<cl-flex1 />
|
||||
|
||||
<el-button type="success" :disabled="!isSelected" @click="confirm()"
|
||||
>使用选中文件 {{ selection.length }}/{{ limit }}</el-button
|
||||
>
|
||||
|
||||
<el-button type="danger" :disabled="!isSelected" @click="remove()"
|
||||
>删除选中文件</el-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 文件区域 -->
|
||||
<div
|
||||
class="cl-upload-space__file scroller1"
|
||||
v-infinite-scroll="loadmore"
|
||||
v-loading="loading"
|
||||
>
|
||||
<!-- 文件列表 -->
|
||||
<template v-if="list.length > 0">
|
||||
<div class="cl-upload-space__file-list">
|
||||
<div
|
||||
class="cl-upload-space__file-item"
|
||||
v-for="item in list"
|
||||
:key="item.preload || item.url"
|
||||
>
|
||||
<space-file
|
||||
:data="item"
|
||||
:list="list"
|
||||
@select="select"
|
||||
@remove="remove"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<!-- 空态 -->
|
||||
<div v-else class="cl-upload-space__file-empty">
|
||||
<el-icon class="el-icon--upload">
|
||||
<upload-filled />
|
||||
</el-icon>
|
||||
<p>将文件拖到此处,或点击按钮上传</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 展开按钮 -->
|
||||
<template #slot-expand>
|
||||
<button class="cl-dialog__controls-icon">
|
||||
<el-icon @click="category.visible = false" v-if="category.visible">
|
||||
<notebook />
|
||||
</el-icon>
|
||||
<el-icon @click="category.visible = true" v-else>
|
||||
<arrow-left />
|
||||
</el-icon>
|
||||
</button>
|
||||
<template #footer>
|
||||
<el-button @click="close">取消</el-button>
|
||||
<el-button :disabled="selection.length == 0" type="success" @click="confirm"
|
||||
>选择 {{ selection.length }}/{{ limit }}</el-button
|
||||
>
|
||||
</template>
|
||||
</cl-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="cl-upload-space">
|
||||
import { computed, provide, reactive, ref, watch } from "vue";
|
||||
import { isEmpty } from "lodash-es";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { Notebook, ArrowLeft, UploadFilled } from "@element-plus/icons-vue";
|
||||
import { module, useCool } from "/@/cool";
|
||||
import { useBase } from "/$/base";
|
||||
import SpaceCategory from "./space/category.vue";
|
||||
import SpaceFile from "./space/file.vue";
|
||||
import { computed, nextTick, ref } from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
// 绑定值
|
||||
modelValue: String,
|
||||
defineProps({
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: "文件空间"
|
||||
},
|
||||
// 可选数量
|
||||
limit: Number,
|
||||
// 是否禁用
|
||||
disabled: Boolean,
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 9
|
||||
},
|
||||
// 类型
|
||||
accept: String,
|
||||
// 显示按钮
|
||||
showBtn: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
// 标题
|
||||
title: {
|
||||
type: String,
|
||||
default: "文件空间"
|
||||
}
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue", "confirm"]);
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
// 缓存
|
||||
const { app } = useBase();
|
||||
|
||||
// 模块配置
|
||||
const { options } = module.get("upload");
|
||||
|
||||
// cl-upload
|
||||
const Upload = ref();
|
||||
|
||||
// 选择图片的数量
|
||||
const limit = props.limit || options.limit?.select;
|
||||
const emit = defineEmits(["confirm"]);
|
||||
|
||||
// 是否可见
|
||||
const visible = ref(false);
|
||||
|
||||
// 是否加载中
|
||||
const loading = ref(false);
|
||||
// cl-upload-panel
|
||||
const Panel = ref();
|
||||
|
||||
// 已选列表
|
||||
const selection = ref<Eps.SpaceInfoEntity[]>([]);
|
||||
|
||||
// 文件列表
|
||||
const list = ref<Eps.SpaceInfoEntity[]>([]);
|
||||
|
||||
// 类目数据
|
||||
const category = reactive({
|
||||
id: null,
|
||||
visible: true
|
||||
});
|
||||
|
||||
// 分页信息
|
||||
const pagination = reactive({
|
||||
page: 1,
|
||||
size: 20,
|
||||
total: 0
|
||||
});
|
||||
|
||||
// 监听屏幕大小变化
|
||||
watch(
|
||||
() => app.browser.isMini,
|
||||
(val) => {
|
||||
category.visible = val ? false : true;
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
);
|
||||
|
||||
// 是否选中
|
||||
const isSelected = computed(() => !isEmpty(selection.value));
|
||||
|
||||
// 打开
|
||||
let lock = false;
|
||||
// 选中列表
|
||||
const selection = computed<any[]>(() => Panel.value?.selection || []);
|
||||
|
||||
function open() {
|
||||
visible.value = true;
|
||||
|
||||
if (!lock) {
|
||||
lock = true;
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
// 清空选择
|
||||
function clear() {
|
||||
selection.value = [];
|
||||
nextTick(() => {
|
||||
Panel.value?.clear();
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭
|
||||
function close() {
|
||||
visible.value = false;
|
||||
clear();
|
||||
}
|
||||
|
||||
// 上传成功
|
||||
function onSuccess(data: any) {
|
||||
service.space.info
|
||||
.add({
|
||||
classifyId: category.id,
|
||||
...data
|
||||
})
|
||||
.then((res) => {
|
||||
data.id = res.id;
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
}
|
||||
|
||||
// 上传时
|
||||
function onUpload(data: any) {
|
||||
list.value.unshift(data);
|
||||
}
|
||||
|
||||
const reqParams = {
|
||||
page: 1
|
||||
};
|
||||
|
||||
// 刷新资源文件
|
||||
async function refresh(params: any = {}) {
|
||||
// 清空选择
|
||||
clear();
|
||||
|
||||
// 合并参数
|
||||
Object.assign(reqParams, {
|
||||
type: props.accept?.split("/")[0],
|
||||
...pagination,
|
||||
...params,
|
||||
classifyId: category.id
|
||||
});
|
||||
|
||||
// 加载中
|
||||
if (reqParams.page == 1) {
|
||||
loading.value = true;
|
||||
}
|
||||
|
||||
await service.space.info.page(reqParams).then((res) => {
|
||||
// 合并
|
||||
Object.assign(pagination, res.pagination);
|
||||
|
||||
if (reqParams.page == 1) {
|
||||
list.value = [];
|
||||
}
|
||||
|
||||
list.value.push(...res.list);
|
||||
});
|
||||
|
||||
// 加载完成
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 确认选中
|
||||
// 确认
|
||||
function confirm() {
|
||||
emit("update:modelValue", selection.value.map((e) => e.url).join(","));
|
||||
emit("confirm", selection.value);
|
||||
|
||||
close();
|
||||
}
|
||||
|
||||
// 选择
|
||||
function select(item: Eps.SpaceInfoEntity) {
|
||||
const index = selection.value.findIndex((e) => e.id === item.id);
|
||||
|
||||
if (index >= 0) {
|
||||
selection.value.splice(index, 1);
|
||||
} else {
|
||||
if (selection.value.length < limit) {
|
||||
selection.value.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除选中
|
||||
function remove(item?: Eps.SpaceInfoEntity) {
|
||||
// 已选文件 id
|
||||
const ids = item ? [item.id] : selection.value.map((e) => e.id);
|
||||
|
||||
ElMessageBox.confirm("此操作将删除文件, 是否继续?", "提示", {
|
||||
type: "warning"
|
||||
})
|
||||
.then(() => {
|
||||
ElMessage.success("删除成功");
|
||||
|
||||
// 删除文件及选择
|
||||
ids.forEach((id) => {
|
||||
[list.value, selection.value].forEach((list) => {
|
||||
const index = list.findIndex((e) => e.id === id);
|
||||
list.splice(index, 1);
|
||||
});
|
||||
});
|
||||
|
||||
// 删除请求
|
||||
service.space.info
|
||||
.delete({
|
||||
ids
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message);
|
||||
});
|
||||
})
|
||||
.catch(() => null);
|
||||
}
|
||||
|
||||
function onDragover(e: any) {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
function onDrop(e: any) {
|
||||
e.preventDefault();
|
||||
|
||||
e.dataTransfer.files.forEach((f: File, i: number) => {
|
||||
setTimeout(() => {
|
||||
Upload.value.upload(f);
|
||||
}, i * 10);
|
||||
});
|
||||
}
|
||||
|
||||
// 加载更多
|
||||
function loadmore() {
|
||||
if (list.value.length && list.value.length < pagination.total) {
|
||||
refresh({
|
||||
page: pagination.page + 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 共享
|
||||
provide("space", {
|
||||
category,
|
||||
selection,
|
||||
refresh,
|
||||
loading,
|
||||
list
|
||||
});
|
||||
|
||||
defineExpose({
|
||||
open,
|
||||
close,
|
||||
clear,
|
||||
refresh
|
||||
close
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.cl-upload-space {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: #f7f7f7;
|
||||
padding: 5px;
|
||||
user-select: none;
|
||||
|
||||
&__dialog {
|
||||
.el-dialog__body {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&__content {
|
||||
flex: 1;
|
||||
max-width: 100%;
|
||||
box-sizing: border-box;
|
||||
background-color: var(--el-bg-color);
|
||||
border-radius: 5px;
|
||||
}
|
||||
|
||||
&__header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 50px;
|
||||
overflow: auto hidden;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
&__file {
|
||||
height: calc(100% - 50px);
|
||||
padding: 0 10px;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
&-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
&-item {
|
||||
height: 150px;
|
||||
width: 150px;
|
||||
margin: 0 10px 10px 0;
|
||||
}
|
||||
|
||||
&-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
top: calc(50% - 90px);
|
||||
left: calc(50% - 180px);
|
||||
height: 180px;
|
||||
width: 360px;
|
||||
border-radius: 5px;
|
||||
border: 2px dashed var(--el-border-color);
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-color: var(--color-primary);
|
||||
}
|
||||
|
||||
i {
|
||||
font-size: 67px;
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 14px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&__footer {
|
||||
padding: 9px 0;
|
||||
}
|
||||
|
||||
&.is-mini {
|
||||
.cl-upload-space__file-list {
|
||||
justify-content: center;
|
||||
}
|
||||
.cl-upload-space__dialog {
|
||||
.el-dialog__footer {
|
||||
border-top: 1px solid #f7f7f7;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -5,12 +5,11 @@
|
|||
:class="[
|
||||
`cl-upload--${type}`,
|
||||
{
|
||||
'is-slot': $slots.default,
|
||||
'is-disabled': disabled
|
||||
}
|
||||
]"
|
||||
>
|
||||
<div class="cl-upload__header">
|
||||
<div class="cl-upload__file-btn" v-if="type == 'file'">
|
||||
<el-upload
|
||||
ref="Upload"
|
||||
action=""
|
||||
|
@ -23,18 +22,7 @@
|
|||
:disabled="disabled"
|
||||
>
|
||||
<slot>
|
||||
<template v-if="type == 'image' && isAdd">
|
||||
<div class="cl-upload__item">
|
||||
<el-icon :size="24"><picture-filled /></el-icon>
|
||||
<span class="cl-upload__text">{{ text }}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template v-if="type == 'file'">
|
||||
<div class="cl-upload__btn">
|
||||
<el-button type="success">{{ text }}</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-button type="success">{{ text }}</el-button>
|
||||
</slot>
|
||||
</el-upload>
|
||||
</div>
|
||||
|
@ -47,7 +35,31 @@
|
|||
v-bind="drag.options"
|
||||
item-key="uid"
|
||||
@end="update"
|
||||
v-if="showFileList"
|
||||
>
|
||||
<template #footer>
|
||||
<div class="cl-upload__footer" v-if="type == 'image' && isAdd">
|
||||
<el-upload
|
||||
ref="Upload"
|
||||
action=""
|
||||
:accept="accept"
|
||||
:show-file-list="false"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="httpRequest"
|
||||
:headers="headers"
|
||||
:multiple="multiple"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<slot>
|
||||
<div class="cl-upload__item">
|
||||
<el-icon :size="24"><picture-filled /></el-icon>
|
||||
<span class="cl-upload__text">{{ text }}</span>
|
||||
</div>
|
||||
</slot>
|
||||
</el-upload>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template #item="{ element: item, index }">
|
||||
<el-upload
|
||||
action=""
|
||||
|
@ -126,18 +138,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<el-image-viewer
|
||||
v-if="pv.visible"
|
||||
:url-list="pv.urls"
|
||||
:initial-index="pv.index"
|
||||
infinite
|
||||
teleported
|
||||
@close="
|
||||
() => {
|
||||
pv.visible = false;
|
||||
}
|
||||
"
|
||||
></el-image-viewer>
|
||||
<item-viewer ref="Viewer" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="cl-upload">
|
||||
|
@ -148,11 +149,11 @@ import Draggable from "vuedraggable";
|
|||
import { ElMessage } from "element-plus";
|
||||
import { PictureFilled, ZoomIn, Delete } from "@element-plus/icons-vue";
|
||||
import { useCool, module } from "/@/cool";
|
||||
import { extname, uuid } from "/@/cool/utils";
|
||||
import { extname, uuid, isPromise } from "/@/cool/utils";
|
||||
import { useBase } from "/$/base";
|
||||
import { fileSize, fileName, fileType } from "../utils";
|
||||
import { useForm } from "@cool-vue/crud";
|
||||
import { fileSize, fileName, fileType, getUrls } from "../utils";
|
||||
import { Upload } from "../types";
|
||||
import ItemViewer from "./items/viewer.vue";
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
|
@ -167,6 +168,10 @@ const props = defineProps({
|
|||
multiple: Boolean,
|
||||
limit: Number,
|
||||
limitSize: Number,
|
||||
limitUpload: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
size: [String, Number, Array],
|
||||
text: String,
|
||||
prefixPath: {
|
||||
|
@ -177,30 +182,30 @@ const props = defineProps({
|
|||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
drag: Boolean,
|
||||
draggable: Boolean,
|
||||
disabled: Boolean,
|
||||
customClass: String,
|
||||
beforeUpload: Function,
|
||||
|
||||
// 穿透值
|
||||
isEdit: null,
|
||||
scope: null
|
||||
scope: null,
|
||||
isDisabled: Boolean
|
||||
});
|
||||
|
||||
const emit = defineEmits(["update:modelValue", "upload", "success", "error", "progress"]);
|
||||
|
||||
const { service } = useCool();
|
||||
|
||||
// 缓存
|
||||
const { user } = useBase();
|
||||
|
||||
// 表单
|
||||
const Form = useForm();
|
||||
|
||||
// 模块配置
|
||||
const { options } = module.get("upload");
|
||||
|
||||
// el-upload
|
||||
const Upload = ref<any>();
|
||||
const Upload = ref();
|
||||
|
||||
// item-viewer
|
||||
const Viewer = ref();
|
||||
|
||||
// 元素尺寸
|
||||
const size = computed(() => {
|
||||
|
@ -210,7 +215,7 @@ const size = computed(() => {
|
|||
|
||||
// 是否禁用
|
||||
const disabled = computed(() => {
|
||||
return Form.value?.disabled || props.disabled;
|
||||
return props.isDisabled || props.disabled;
|
||||
});
|
||||
|
||||
// 最大上传数量
|
||||
|
@ -229,25 +234,18 @@ const headers = computed(() => {
|
|||
};
|
||||
});
|
||||
|
||||
// 预览
|
||||
const pv = reactive<{ visible: boolean; urls: string[]; index: number }>({
|
||||
visible: false,
|
||||
urls: [],
|
||||
index: 0
|
||||
});
|
||||
|
||||
// 列表
|
||||
const list = ref<Upload.Item[]>([]);
|
||||
|
||||
// 拖拽
|
||||
const drag = reactive<any>({
|
||||
const drag = reactive({
|
||||
options: {
|
||||
group: "Upload",
|
||||
animation: 300,
|
||||
ghostClass: "Ghost",
|
||||
dragClass: "Drag",
|
||||
draggable: ".is-drag",
|
||||
disabled: !props.drag
|
||||
disabled: !props.draggable
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -271,35 +269,56 @@ function getType(path: string) {
|
|||
}
|
||||
|
||||
// 上传前
|
||||
function beforeUpload(file: any, item?: Upload.Item) {
|
||||
if (file.size / 1024 / 1024 >= limitSize) {
|
||||
ElMessage.error(`上传文件大小不能超过 ${limitSize}MB!`);
|
||||
return false;
|
||||
}
|
||||
async function beforeUpload(file: any, item?: Upload.Item) {
|
||||
function next() {
|
||||
const d = {
|
||||
type: getType(file.name),
|
||||
preload: "",
|
||||
progress: 0,
|
||||
url: "",
|
||||
uid: file.uid,
|
||||
size: file.size
|
||||
};
|
||||
|
||||
const d = {
|
||||
type: getType(file.name),
|
||||
preload: "",
|
||||
progress: 0,
|
||||
url: "",
|
||||
uid: file.uid,
|
||||
size: file.size
|
||||
};
|
||||
d.preload = d.type == "image" ? window.webkitURL.createObjectURL(file) : file.name;
|
||||
|
||||
d.preload = d.type == "image" ? window.webkitURL.createObjectURL(file) : file.name;
|
||||
|
||||
if (!item) {
|
||||
if (isAdd.value) {
|
||||
list.value.push(d);
|
||||
if (!item) {
|
||||
if (props.multiple) {
|
||||
if (isAdd.value || !props.limitUpload) {
|
||||
list.value.push(d);
|
||||
}
|
||||
} else {
|
||||
list.value = [d];
|
||||
}
|
||||
} else {
|
||||
list.value = [d];
|
||||
Object.assign(item, d);
|
||||
}
|
||||
} else {
|
||||
Object.assign(item, d);
|
||||
|
||||
emit("upload", d);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
emit("upload", d);
|
||||
return true;
|
||||
if (props.beforeUpload) {
|
||||
const r = props.beforeUpload(file, item);
|
||||
|
||||
if (isPromise(r)) {
|
||||
r.then(next).catch(() => null);
|
||||
} else {
|
||||
if (r) {
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
return r;
|
||||
} else {
|
||||
if (file.size / 1024 / 1024 >= limitSize) {
|
||||
ElMessage.error(`上传文件大小不能超过 ${limitSize}MB!`);
|
||||
return false;
|
||||
}
|
||||
|
||||
return next();
|
||||
}
|
||||
}
|
||||
|
||||
// 移除
|
||||
|
@ -316,9 +335,10 @@ function clear() {
|
|||
// 预览
|
||||
function preview(item: Upload.Item) {
|
||||
if (item.type == "image") {
|
||||
pv.visible = true;
|
||||
pv.urls = list.value.map((e) => e.preload);
|
||||
pv.index = pv.urls.indexOf(item.preload);
|
||||
Viewer.value?.open(
|
||||
item.preload,
|
||||
list.value.map((e) => e.preload)
|
||||
);
|
||||
} else {
|
||||
window.open(item.url);
|
||||
}
|
||||
|
@ -330,6 +350,10 @@ async function httpRequest(req: any, item?: any) {
|
|||
item = list.value.find((e) => e.uid == req.file.uid);
|
||||
}
|
||||
|
||||
if (!item) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
// 文件名 uuid + filename
|
||||
let fileName = uuid() + "_" + req.file.name;
|
||||
|
@ -450,10 +474,7 @@ function update() {
|
|||
const check = list.value.find((e) => !e.url);
|
||||
|
||||
if (!check) {
|
||||
emit(
|
||||
"update:modelValue",
|
||||
list.value.map((e) => e.url.replace(/,/g, encodeURIComponent(","))).join(",")
|
||||
);
|
||||
emit("update:modelValue", getUrls(list.value));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -506,17 +527,8 @@ defineExpose({
|
|||
|
||||
<style lang="scss" scoped>
|
||||
.cl-upload {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
line-height: normal;
|
||||
|
||||
&.is-disabled {
|
||||
:deep(.cl-upload__item) {
|
||||
cursor: not-allowed;
|
||||
background-color: var(--el-disabled-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.Ghost {
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
@ -527,26 +539,13 @@ defineExpose({
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&--file {
|
||||
.cl-upload__list {
|
||||
width: 100%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
&__header {
|
||||
.cl-upload__item {
|
||||
margin-right: 5px;
|
||||
}
|
||||
&__file {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.cl-upload__item {
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
&__text {
|
||||
|
@ -596,6 +595,7 @@ defineExpose({
|
|||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
user-select: none;
|
||||
margin: 0 5px 5px 0;
|
||||
|
||||
&:hover {
|
||||
border-color: currentColor;
|
||||
|
@ -658,13 +658,16 @@ defineExpose({
|
|||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&.is-slot {
|
||||
&--file {
|
||||
.cl-upload__list {
|
||||
margin: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.un-drag {
|
||||
display: flex;
|
||||
&.is-disabled {
|
||||
:deep(.cl-upload__item) {
|
||||
cursor: not-allowed;
|
||||
background-color: var(--el-disabled-bg-color);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,10 @@
|
|||
export default () => {
|
||||
return {
|
||||
components: [import("./components/index.vue"), import("./components/space.vue")],
|
||||
components: [
|
||||
import("./components/upload.vue"),
|
||||
import("./components/space.vue"),
|
||||
import("./components/panel.vue")
|
||||
],
|
||||
|
||||
options: {
|
||||
// 尺寸
|
||||
|
@ -11,11 +15,19 @@ export default () => {
|
|||
limit: {
|
||||
// 上传最大数量
|
||||
upload: 9,
|
||||
// 文件空间选择数
|
||||
select: 9,
|
||||
// 上传大小限制
|
||||
size: 100
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
views: [
|
||||
{
|
||||
meta: {
|
||||
label: "图片空间"
|
||||
},
|
||||
path: "/upload/list",
|
||||
component: () => import("./views/list.vue")
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
|
|
@ -60,3 +60,8 @@ export function fileType(path: string) {
|
|||
export function fileRule(value: string) {
|
||||
return fileRules.find((e) => e.value == value);
|
||||
}
|
||||
|
||||
// 拼接数组下的url
|
||||
export function getUrls(list: any[]) {
|
||||
return list.map((e) => e.url.replace(/,/g, encodeURIComponent(","))).join(",");
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<panel />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="upload-list">
|
||||
import Panel from "../components/panel.vue";
|
||||
</script>
|
File diff suppressed because one or more lines are too long
|
@ -3,6 +3,7 @@ import { UserConfig } from "vite";
|
|||
import vue from "@vitejs/plugin-vue";
|
||||
import vueJsx from "@vitejs/plugin-vue-jsx";
|
||||
import compression from "vite-plugin-compression";
|
||||
import { visualizer } from "rollup-plugin-visualizer";
|
||||
import { proxy } from "./src/cool/config/proxy";
|
||||
import { cool } from "./build/cool";
|
||||
|
||||
|
@ -15,7 +16,17 @@ function resolve(dir: string) {
|
|||
export default (): UserConfig => {
|
||||
return {
|
||||
base: "/",
|
||||
plugins: [vue(), compression(), vueJsx(), cool()],
|
||||
plugins: [
|
||||
vue(),
|
||||
compression(),
|
||||
vueJsx(),
|
||||
cool(),
|
||||
visualizer({
|
||||
open: false,
|
||||
gzipSize: true,
|
||||
brotliSize: true
|
||||
})
|
||||
],
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
|
|
260
yarn.lock
260
yarn.lock
|
@ -965,101 +965,14 @@
|
|||
"@babel/helper-validator-identifier" "^7.19.1"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@codemirror/autocomplete@^6.0.0":
|
||||
version "6.4.0"
|
||||
resolved "https://registry.npmjs.org/@codemirror/autocomplete/-/autocomplete-6.4.0.tgz#76ac9a2a411a4cc6e13103014dba5e0fe601da5a"
|
||||
integrity sha512-HLF2PnZAm1s4kGs30EiqKMgD7XsYaQ0XJnMR0rofEWQ5t5D60SfqpDIkIh1ze5tiEbyUWm8+VJ6W1/erVvBMIA==
|
||||
dependencies:
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.6.0"
|
||||
"@lezer/common" "^1.0.0"
|
||||
|
||||
"@codemirror/commands@6.x", "@codemirror/commands@^6.0.0":
|
||||
version "6.1.3"
|
||||
resolved "https://registry.npmjs.org/@codemirror/commands/-/commands-6.1.3.tgz#401d0b6d18e7d5eb9a96f6c8ae4ea56a08e8fd06"
|
||||
integrity sha512-wUw1+vb34Ultv0Q9m/OVB7yizGXgtoDbkI5f5ErM8bebwLyUYjicdhJTKhTvPTpgkv8dq/BK0lQ3K5pRf2DAJw==
|
||||
dependencies:
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.2.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
"@lezer/common" "^1.0.0"
|
||||
|
||||
"@codemirror/lang-javascript@^6.1.2":
|
||||
version "6.1.2"
|
||||
resolved "https://registry.npmjs.org/@codemirror/lang-javascript/-/lang-javascript-6.1.2.tgz#a11812ca1d21301cdeb80e51b4c007edcf55f813"
|
||||
integrity sha512-OcwLfZXdQ1OHrLiIcKCn7MqZ7nx205CMKlhe+vL88pe2ymhT9+2P+QhwkYGxMICj8TDHyp8HFKVwpiisUT7iEQ==
|
||||
dependencies:
|
||||
"@codemirror/autocomplete" "^6.0.0"
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/lint" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
"@lezer/common" "^1.0.0"
|
||||
"@lezer/javascript" "^1.0.0"
|
||||
|
||||
"@codemirror/language@6.x", "@codemirror/language@^6.0.0":
|
||||
version "6.3.2"
|
||||
resolved "https://registry.npmjs.org/@codemirror/language/-/language-6.3.2.tgz#a3d5796d17a2cd3110bac0f5126db67c7e90a0f3"
|
||||
integrity sha512-g42uHhOcEMAXjmozGG+rdom5UsbyfMxQFh7AbkeoaNImddL6Xt4cQDL0+JxmG7+as18rUAvZaqzP/TjsciVIrA==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
"@lezer/common" "^1.0.0"
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
"@lezer/lr" "^1.0.0"
|
||||
style-mod "^4.0.0"
|
||||
|
||||
"@codemirror/lint@^6.0.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/@codemirror/lint/-/lint-6.1.0.tgz#f006142d3a580fdb8ffc2faa3361b2232c08e079"
|
||||
integrity sha512-mdvDQrjRmYPvQ3WrzF6Ewaao+NWERYtpthJvoQ3tK3t/44Ynhk8ZGjTSL9jMEv8CgSMogmt75X8ceOZRDSXHtQ==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
crelt "^1.0.5"
|
||||
|
||||
"@codemirror/search@^6.0.0":
|
||||
version "6.2.3"
|
||||
resolved "https://registry.npmjs.org/@codemirror/search/-/search-6.2.3.tgz#fab933fef1b1de8ef40cda275c73d9ac7a1ff40f"
|
||||
integrity sha512-V9n9233lopQhB1dyjsBK2Wc1i+8hcCqxl1wQ46c5HWWLePoe4FluV3TGHoZ04rBRlGjNyz9DTmpJErig8UE4jw==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
crelt "^1.0.5"
|
||||
|
||||
"@codemirror/state@6.x", "@codemirror/state@^6.0.0", "@codemirror/state@^6.1.4", "@codemirror/state@^6.2.0":
|
||||
version "6.2.0"
|
||||
resolved "https://registry.npmjs.org/@codemirror/state/-/state-6.2.0.tgz#a0fb08403ced8c2a68d1d0acee926bd20be922f2"
|
||||
integrity sha512-69QXtcrsc3RYtOtd+GsvczJ319udtBf1PTrr2KbLWM/e2CXUPnh0Nz9AUo8WfhSQ7GeL8dPVNUmhQVgpmuaNGA==
|
||||
|
||||
"@codemirror/theme-one-dark@^6.1.0":
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/@codemirror/theme-one-dark/-/theme-one-dark-6.1.0.tgz#6f8b3c7fc22e9fec59edd573f4ba9546db42e007"
|
||||
integrity sha512-AiTHtFRu8+vWT9wWUWDM+cog6ZwgivJogB1Tm/g40NIpLwph7AnmxrSzWfvJN5fBVufsuwBxecQCNmdcR5D7Aw==
|
||||
dependencies:
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
|
||||
"@codemirror/view@6.x", "@codemirror/view@^6.0.0", "@codemirror/view@^6.6.0":
|
||||
version "6.7.1"
|
||||
resolved "https://registry.npmjs.org/@codemirror/view/-/view-6.7.1.tgz#370e95d6f001e7f5cadc459807974b4f0a6eb225"
|
||||
integrity sha512-kYtS+uqYw/q/0ytYxpkqE1JVuK5NsbmBklWYhwLFTKO9gVuTdh/kDEeZPKorbqHcJ+P+ucrhcsS1czVweOpT2g==
|
||||
dependencies:
|
||||
"@codemirror/state" "^6.1.4"
|
||||
style-mod "^4.0.0"
|
||||
w3c-keyname "^2.2.4"
|
||||
|
||||
"@cool-vue/crud@^5.9.4":
|
||||
version "5.9.4"
|
||||
resolved "https://registry.npmjs.org/@cool-vue/crud/-/crud-5.9.4.tgz#df5d3a28565a0b4fb7167449a2ace1c14cbf6a2c"
|
||||
integrity sha512-PoAs2yyP4NunchnlgD7iIj5H64Ed2D7s1x3FvE7wpxPsGZs6LDEwJwETI85A6MbNi32uw72KA0t4mUNkRGR8HQ==
|
||||
"@cool-vue/crud@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/@cool-vue/crud/-/crud-6.0.1.tgz#26f89b05a939d3dcd316bf00fd4cf88c14e4c193"
|
||||
integrity sha512-LjcS5dvOpE/RUzIk2bSBawVk+NMMxj7MFPYXMdCPST8loPwOSqoCeIfkCwkJP8m4S4fdhvCCpSoRmY97ezhrAA==
|
||||
dependencies:
|
||||
array.prototype.flat "^1.2.4"
|
||||
core-js "^3.21.1"
|
||||
element-plus "^2.2.27"
|
||||
element-plus "^2.2.28"
|
||||
merge "^2.1.1"
|
||||
mitt "^3.0.0"
|
||||
vue "^3.2.45"
|
||||
|
@ -1290,33 +1203,6 @@
|
|||
"@jridgewell/resolve-uri" "3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "1.4.14"
|
||||
|
||||
"@lezer/common@^1.0.0":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/@lezer/common/-/common-1.0.2.tgz#8fb9b86bdaa2ece57e7d59e5ffbcb37d71815087"
|
||||
integrity sha512-SVgiGtMnMnW3ActR8SXgsDhw7a0w0ChHSYAyAUxxrOiJ1OqYWEKk/xJd84tTSPo1mo6DXLObAJALNnd0Hrv7Ng==
|
||||
|
||||
"@lezer/highlight@^1.0.0":
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/@lezer/highlight/-/highlight-1.1.3.tgz#bf5a36c2ee227f526d74997ac91f7777e29bd25d"
|
||||
integrity sha512-3vLKLPThO4td43lYRBygmMY18JN3CPh9w+XS2j8WC30vR4yZeFG4z1iFe4jXE43NtGqe//zHW5q8ENLlHvz9gw==
|
||||
dependencies:
|
||||
"@lezer/common" "^1.0.0"
|
||||
|
||||
"@lezer/javascript@^1.0.0":
|
||||
version "1.4.0"
|
||||
resolved "https://registry.npmjs.org/@lezer/javascript/-/javascript-1.4.0.tgz#fe71474fcadc6112fb0978310faed0788d0af824"
|
||||
integrity sha512-MQ3oLJGEtpUgZ03LOLI60tDnjSkKO6h9hZSe31qJ1UQV+I9bpv3pwSnPUnX0+e+3E1PBVkox0GB2/MXkxg0M2w==
|
||||
dependencies:
|
||||
"@lezer/highlight" "^1.0.0"
|
||||
"@lezer/lr" "^1.0.0"
|
||||
|
||||
"@lezer/lr@^1.0.0":
|
||||
version "1.2.5"
|
||||
resolved "https://registry.npmjs.org/@lezer/lr/-/lr-1.2.5.tgz#e9088164a711690596f17378665e0554157c9b03"
|
||||
integrity sha512-f9319YG1A/3ysgUE3bqCHEd7g+3ZZ71MWlwEc42mpnLVYXgfJJgtu1XAyBB4Kz8FmqmnFe9caopDqKeMMMAU6g==
|
||||
dependencies:
|
||||
"@lezer/common" "^1.0.0"
|
||||
|
||||
"@node-ipc/js-queue@2.0.3":
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/@node-ipc/js-queue/-/js-queue-2.0.3.tgz#ac7fe33d766fa53e233ef8fedaf3443a01c5a4cd"
|
||||
|
@ -1450,7 +1336,7 @@
|
|||
resolved "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0"
|
||||
integrity sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==
|
||||
|
||||
"@types/quill@^2.0.9":
|
||||
"@types/quill@^2.0.10":
|
||||
version "2.0.10"
|
||||
resolved "https://registry.npmjs.org/@types/quill/-/quill-2.0.10.tgz#bd3df7b59e0cfeb90f3d38892e4d1866261e7c0e"
|
||||
integrity sha512-L6OHONEj2v4NRbWQOsn7j1N0SyzhRR3M4g1M6j/uuIwIsIW2ShWHhwbqNvH8hSmVktzqu0lITfdnqVOQ4qkrhA==
|
||||
|
@ -2168,7 +2054,7 @@ ansi-styles@^3.2.1:
|
|||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-styles@^4.1.0:
|
||||
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
|
||||
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
|
||||
|
@ -2424,6 +2310,15 @@ cli-spinners@^2.5.0:
|
|||
resolved "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.7.0.tgz#f815fd30b5f9eaac02db604c7a231ed7cb2f797a"
|
||||
integrity sha512-qu3pN8Y3qHNgE2AFweciB1IfMnmZ/fsNTEE+NOFjmGB2F/7rLhnhzppvpCnN4FovtP26k8lHyy9ptEbNwWFLzw==
|
||||
|
||||
cliui@^8.0.1:
|
||||
version "8.0.1"
|
||||
resolved "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa"
|
||||
integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==
|
||||
dependencies:
|
||||
string-width "^4.2.0"
|
||||
strip-ansi "^6.0.1"
|
||||
wrap-ansi "^7.0.0"
|
||||
|
||||
clone@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||
|
@ -2434,19 +2329,6 @@ clone@^2.1.1:
|
|||
resolved "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f"
|
||||
integrity sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==
|
||||
|
||||
codemirror@^6.0.1:
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/codemirror/-/codemirror-6.0.1.tgz#62b91142d45904547ee3e0e0e4c1a79158035a29"
|
||||
integrity sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==
|
||||
dependencies:
|
||||
"@codemirror/autocomplete" "^6.0.0"
|
||||
"@codemirror/commands" "^6.0.0"
|
||||
"@codemirror/language" "^6.0.0"
|
||||
"@codemirror/lint" "^6.0.0"
|
||||
"@codemirror/search" "^6.0.0"
|
||||
"@codemirror/state" "^6.0.0"
|
||||
"@codemirror/view" "^6.0.0"
|
||||
|
||||
codepage@~1.15.0:
|
||||
version "1.15.0"
|
||||
resolved "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz#2e00519024b39424ec66eeb3ec07227e692618ab"
|
||||
|
@ -2541,11 +2423,6 @@ crc-32@~1.2.0, crc-32@~1.2.1:
|
|||
resolved "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
|
||||
integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
|
||||
|
||||
crelt@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94"
|
||||
integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==
|
||||
|
||||
cross-spawn@^6.0.0:
|
||||
version "6.0.5"
|
||||
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
|
||||
|
@ -2682,7 +2559,7 @@ electron-to-chromium@^1.4.251:
|
|||
resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.284.tgz#61046d1e4cab3a25238f6bf7413795270f125592"
|
||||
integrity sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==
|
||||
|
||||
element-plus@^2.2.27, element-plus@^2.2.28:
|
||||
element-plus@^2.2.28:
|
||||
version "2.2.28"
|
||||
resolved "https://registry.npmjs.org/element-plus/-/element-plus-2.2.28.tgz#855441976e82da597faecaf6ed74fc4650a970b2"
|
||||
integrity sha512-BsxF7iEaBydmRfw1Tt++EO9jRBjbtJr7ZRIrnEwz4J3Cwa1IzHCNCcx3ZwcYTlJq9CYFxv94JnbNr1EbkTou3A==
|
||||
|
@ -2703,6 +2580,11 @@ element-plus@^2.2.27, element-plus@^2.2.28:
|
|||
memoize-one "^6.0.0"
|
||||
normalize-wheel-es "^1.2.0"
|
||||
|
||||
emoji-regex@^8.0.0:
|
||||
version "8.0.0"
|
||||
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
|
||||
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
|
||||
|
||||
emojis-list@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
|
||||
|
@ -3263,6 +3145,11 @@ gensync@^1.0.0-beta.2:
|
|||
resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
|
||||
integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
|
||||
|
||||
get-caller-file@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
|
||||
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.3.tgz#063c84329ad93e83893c7f4f243ef63ffa351385"
|
||||
|
@ -3546,6 +3433,11 @@ is-extglob@^2.1.1:
|
|||
resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
|
||||
integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
|
||||
|
||||
is-fullwidth-code-point@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
|
||||
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
|
||||
|
||||
is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
|
||||
|
@ -3974,6 +3866,11 @@ mockjs@^1.1.0:
|
|||
dependencies:
|
||||
commander "*"
|
||||
|
||||
monaco-editor@^0.34.1:
|
||||
version "0.34.1"
|
||||
resolved "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz#1b75c4ad6bc4c1f9da656d740d98e0b850a22f87"
|
||||
integrity sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==
|
||||
|
||||
ms@2.1.2:
|
||||
version "2.1.2"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
|
||||
|
@ -4107,7 +4004,7 @@ onetime@^5.1.0:
|
|||
dependencies:
|
||||
mimic-fn "^2.1.0"
|
||||
|
||||
open@^8.0.2:
|
||||
open@^8.0.2, open@^8.4.0:
|
||||
version "8.4.0"
|
||||
resolved "https://registry.npmjs.org/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
|
||||
integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==
|
||||
|
@ -4445,6 +4342,11 @@ regjsparser@^0.9.1:
|
|||
dependencies:
|
||||
jsesc "~0.5.0"
|
||||
|
||||
require-directory@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
|
||||
integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==
|
||||
|
||||
resize-detector@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.npmjs.org/resize-detector/-/resize-detector-0.3.0.tgz#fe495112e184695500a8f51e0389f15774cb1cfc"
|
||||
|
@ -4484,6 +4386,16 @@ rimraf@^3.0.2:
|
|||
dependencies:
|
||||
glob "^7.1.3"
|
||||
|
||||
rollup-plugin-visualizer@^5.9.0:
|
||||
version "5.9.0"
|
||||
resolved "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.9.0.tgz#013ac54fb6a9d7c9019e7eb77eced673399e5a0b"
|
||||
integrity sha512-bbDOv47+Bw4C/cgs0czZqfm8L82xOZssk4ayZjG40y9zbXclNk7YikrZTDao6p7+HDiGxrN0b65SgZiVm9k1Cg==
|
||||
dependencies:
|
||||
open "^8.4.0"
|
||||
picomatch "^2.3.1"
|
||||
source-map "^0.7.4"
|
||||
yargs "^17.5.1"
|
||||
|
||||
rollup@^3.7.0:
|
||||
version "3.9.0"
|
||||
resolved "https://registry.npmjs.org/rollup/-/rollup-3.9.0.tgz#0ff7ab7cd71ce3a6ab140c5cf661f2b35eb6aab8"
|
||||
|
@ -4697,6 +4609,11 @@ source-map@^0.6.0, source-map@^0.6.1:
|
|||
resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
|
||||
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
|
||||
|
||||
source-map@^0.7.4:
|
||||
version "0.7.4"
|
||||
resolved "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz#a9bbe705c9d8846f4e08ff6765acf0f1b0898656"
|
||||
integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==
|
||||
|
||||
sourcemap-codec@^1.4.8:
|
||||
version "1.4.8"
|
||||
resolved "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
|
||||
|
@ -4745,6 +4662,15 @@ store@^2.0.12:
|
|||
resolved "https://registry.npmjs.org/store/-/store-2.0.12.tgz#8c534e2a0b831f72b75fc5f1119857c44ef5d593"
|
||||
integrity sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==
|
||||
|
||||
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
|
||||
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
|
||||
dependencies:
|
||||
emoji-regex "^8.0.0"
|
||||
is-fullwidth-code-point "^3.0.0"
|
||||
strip-ansi "^6.0.1"
|
||||
|
||||
string.prototype.trimend@^1.0.6:
|
||||
version "1.0.6"
|
||||
resolved "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533"
|
||||
|
@ -4787,11 +4713,6 @@ strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
|
|||
resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
|
||||
integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
|
||||
|
||||
style-mod@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/style-mod/-/style-mod-4.0.0.tgz#97e7c2d68b592975f2ca7a63d0dd6fcacfe35a01"
|
||||
integrity sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==
|
||||
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
|
@ -5038,16 +4959,6 @@ vite@^4.0.3:
|
|||
optionalDependencies:
|
||||
fsevents "~2.3.2"
|
||||
|
||||
vue-codemirror@^6.1.1:
|
||||
version "6.1.1"
|
||||
resolved "https://registry.npmjs.org/vue-codemirror/-/vue-codemirror-6.1.1.tgz#246697ef4cfa6b2448dd592ade214bb7ff86611f"
|
||||
integrity sha512-rTAYo44owd282yVxKtJtnOi7ERAcXTeviwoPXjIc6K/IQYUsoDkzPvw/JDFtSP6T7Cz/2g3EHaEyeyaQCKoDMg==
|
||||
dependencies:
|
||||
"@codemirror/commands" "6.x"
|
||||
"@codemirror/language" "6.x"
|
||||
"@codemirror/state" "6.x"
|
||||
"@codemirror/view" "6.x"
|
||||
|
||||
vue-demi@*, vue-demi@^0.13.2:
|
||||
version "0.13.11"
|
||||
resolved "https://registry.npmjs.org/vue-demi/-/vue-demi-0.13.11.tgz#7d90369bdae8974d87b1973564ad390182410d99"
|
||||
|
@ -5099,11 +5010,6 @@ vuedraggable@^4.1.0:
|
|||
dependencies:
|
||||
sortablejs "1.14.0"
|
||||
|
||||
w3c-keyname@^2.2.4:
|
||||
version "2.2.6"
|
||||
resolved "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.6.tgz#8412046116bc16c5d73d4e612053ea10a189c85f"
|
||||
integrity sha512-f+fciywl1SJEniZHD6H+kUO8gOnwIr7f4ijKA6+ZvJFjeGi1r4PDLl53Ayud9O/rk64RqgoQine0feoeOU0kXg==
|
||||
|
||||
watchpack@^2.4.0:
|
||||
version "2.4.0"
|
||||
resolved "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d"
|
||||
|
@ -5212,6 +5118,15 @@ word@~0.3.0:
|
|||
resolved "https://registry.npmjs.org/word/-/word-0.3.0.tgz#8542157e4f8e849f4a363a288992d47612db9961"
|
||||
integrity sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==
|
||||
|
||||
wrap-ansi@^7.0.0:
|
||||
version "7.0.0"
|
||||
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
|
||||
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
|
||||
dependencies:
|
||||
ansi-styles "^4.0.0"
|
||||
string-width "^4.1.0"
|
||||
strip-ansi "^6.0.0"
|
||||
|
||||
wrappy@1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
|
||||
|
@ -5245,6 +5160,11 @@ xmlhttprequest-ssl@~2.0.0:
|
|||
resolved "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz#91360c86b914e67f44dce769180027c0da618c67"
|
||||
integrity sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==
|
||||
|
||||
y18n@^5.0.5:
|
||||
version "5.0.8"
|
||||
resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
|
||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd"
|
||||
|
@ -5260,6 +5180,24 @@ yaml@^1.7.2:
|
|||
resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
|
||||
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
|
||||
|
||||
yargs-parser@^21.1.1:
|
||||
version "21.1.1"
|
||||
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
||||
|
||||
yargs@^17.5.1:
|
||||
version "17.6.2"
|
||||
resolved "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz#2e23f2944e976339a1ee00f18c77fedee8332541"
|
||||
integrity sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==
|
||||
dependencies:
|
||||
cliui "^8.0.1"
|
||||
escalade "^3.1.1"
|
||||
get-caller-file "^2.0.5"
|
||||
require-directory "^2.1.1"
|
||||
string-width "^4.2.3"
|
||||
y18n "^5.0.5"
|
||||
yargs-parser "^21.1.1"
|
||||
|
||||
yocto-queue@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
|
||||
|
|
Loading…
Reference in New Issue