chore: 🔨 合并冲突解决
This commit is contained in:
commit
b24f77409a
15
package.json
15
package.json
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "vue3-element-admin",
|
||||
"private": true,
|
||||
"version": "2.6.0",
|
||||
"version": "2.6.1",
|
||||
"scripts": {
|
||||
"preinstall": "npx only-allow pnpm",
|
||||
"dev": "vite serve --mode development",
|
||||
|
|
@ -46,9 +46,8 @@
|
|||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "5.1.10",
|
||||
"axios": "^1.4.0",
|
||||
"codemirror": "^5.65.13",
|
||||
"echarts": "^5.2.2",
|
||||
"element-plus": "^2.3.9",
|
||||
"element-plus": "^2.3.12",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
|
|
@ -56,8 +55,9 @@
|
|||
"path-to-regexp": "^6.2.0",
|
||||
"pinia": "^2.0.33",
|
||||
"screenfull": "^6.0.0",
|
||||
"sockjs-client": "1.6.1",
|
||||
"sortablejs": "^1.15.0",
|
||||
"terser": "^5.19.3",
|
||||
"stompjs": "^2.3.3",
|
||||
"vue": "^3.3.4",
|
||||
"vue-i18n": "9.2.2",
|
||||
"vue-router": "^4.2.0",
|
||||
|
|
@ -67,11 +67,12 @@
|
|||
"@commitlint/cli": "^17.6.3",
|
||||
"@commitlint/config-conventional": "^17.6.3",
|
||||
"@iconify-json/ep": "^1.1.10",
|
||||
"@types/codemirror": "^5.60.7",
|
||||
"@types/lodash": "^4.14.195",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
"@types/path-browserify": "^1.0.0",
|
||||
"@types/sockjs-client": "^1.5.1",
|
||||
"@types/sortablejs": "^1.15.1",
|
||||
"@types/stompjs": "^2.3.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.59.6",
|
||||
"@typescript-eslint/parser": "^5.59.6",
|
||||
"autoprefixer": "^10.4.14",
|
||||
|
|
@ -89,7 +90,6 @@
|
|||
"postcss-html": "^1.5.0",
|
||||
"postcss-scss": "^4.0.6",
|
||||
"prettier": "^2.8.8",
|
||||
"rollup-plugin-visualizer": "^5.9.2",
|
||||
"sass": "^1.58.3",
|
||||
"stylelint": "^15.10.2",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
|
|
@ -103,8 +103,7 @@
|
|||
"unplugin-auto-import": "^0.15.3",
|
||||
"unplugin-icons": "^0.16.1",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^4.4.2",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite": "^4.4.9",
|
||||
"vite-plugin-mock": "^3.0.0",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vue-tsc": "^1.7.0 "
|
||||
|
|
|
|||
|
|
@ -1,510 +0,0 @@
|
|||
<template>
|
||||
<div :class="['tagInputarea', className]">
|
||||
<div
|
||||
ref="cmEle"
|
||||
:class="[
|
||||
'tagInputareaIuput',
|
||||
'ThemeBorderColor3',
|
||||
!!readonly ? 'readonlyBg' : '',
|
||||
]"
|
||||
></div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import CodeMirror from "codemirror";
|
||||
import "codemirror/lib/codemirror.css";
|
||||
import { getRePosFromStr, MODE } from "./util";
|
||||
|
||||
const props = defineProps({
|
||||
mode: {
|
||||
type: Number,
|
||||
default: MODE.TEXT,
|
||||
},
|
||||
className: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
defaultValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
required: true,
|
||||
},
|
||||
renderTag: {
|
||||
type: Function,
|
||||
required: true,
|
||||
},
|
||||
minHeight: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
noCursor: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
operatorsSetMargin: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
autoComma: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 是否禁用复制
|
||||
disabledCopy: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emit = defineEmits(["onChange", "onBlur", "onFocus"]);
|
||||
//编辑器挂载dom节点
|
||||
const cmEle = ref();
|
||||
|
||||
//编辑器实例
|
||||
let cmInstance: any = null;
|
||||
|
||||
onMounted(() => {
|
||||
if (cmEle.value) {
|
||||
//编辑器初始化
|
||||
cmInstance = CodeMirror(cmEle.value, {
|
||||
value: props.defaultValue,
|
||||
mode: "",
|
||||
lineWrapping: true,
|
||||
cursorHeight: props.noCursor || props.readonly ? 0 : 1,
|
||||
});
|
||||
//最小高度初始化
|
||||
if (props.minHeight) {
|
||||
let height =
|
||||
typeof props.minHeight === "number"
|
||||
? `${props.minHeight}px`
|
||||
: props.minHeight;
|
||||
//编辑器高度
|
||||
cmInstance.setSize("100%", height);
|
||||
//编辑内容高度
|
||||
let codeEle = cmEle.value.getElementsByClassName("CodeMirror-code")[0];
|
||||
codeEle && codeEle.setAttribute("style", `min-height:${height}`);
|
||||
}
|
||||
//默认值初始化
|
||||
if (props.defaultValue) {
|
||||
updateTextareaView();
|
||||
cmInstance.execCommand("goDocEnd");
|
||||
}
|
||||
//监听 value change
|
||||
cmInstance.on("change", cmChange);
|
||||
//监听 value beforeChange
|
||||
cmInstance.on("beforeChange", cmBeforeChange);
|
||||
//监听表单聚焦
|
||||
cmInstance.on("focus", () => {
|
||||
cmEle.value.classList.add("active");
|
||||
cmFocus();
|
||||
});
|
||||
|
||||
//监听表单失焦
|
||||
cmInstance.on("blur", () => {
|
||||
if (cmEle.value) {
|
||||
cmEle.value.classList.remove("active");
|
||||
}
|
||||
cmBlur();
|
||||
});
|
||||
if (props.disabledCopy) {
|
||||
// 禁止复制,防止tag复制的是id
|
||||
cmInstance.on("copy", (cm: any, e: Event) => {
|
||||
e.preventDefault();
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
//编辑器change
|
||||
const cmChange = (cm: any, obj: any): void => {
|
||||
let value = cm.getValue();
|
||||
if (obj.origin !== "setValue") {
|
||||
emit("onChange", null, value, obj);
|
||||
}
|
||||
|
||||
updateTextareaView();
|
||||
};
|
||||
|
||||
//编辑器beforeChange
|
||||
const cmBeforeChange = (cm: any, obj: any): any => {
|
||||
let { text } = obj;
|
||||
// 如果是自定义公式,只能允许数字+、-、*、/、( \ ) , .,大写字符
|
||||
if (
|
||||
props.mode === MODE.FORMULA &&
|
||||
(obj.origin === "paste" ||
|
||||
obj.origin === "+input" ||
|
||||
obj.origin === "*compose")
|
||||
) {
|
||||
text = text.map((t: string) =>
|
||||
t.replace(/[^+\-*\/0-9/a-z/A-Z\(\)\,\.]/gm, "").toUpperCase()
|
||||
);
|
||||
// 最大输入10000字符
|
||||
if (text.map((t: string) => t).join("").length > 10000) {
|
||||
text = text
|
||||
.map((t: string) => t)
|
||||
.join("")
|
||||
.slice(0, 10000)
|
||||
.split(",");
|
||||
obj.update(obj.from, obj.to, text);
|
||||
obj.cancel();
|
||||
}
|
||||
}
|
||||
|
||||
if (obj.origin === "undo" || obj.origin === "redo") {
|
||||
return;
|
||||
}
|
||||
// 事件内,mode只能从每次props取,不然取不到最新
|
||||
if (
|
||||
props.readonly ||
|
||||
(props.mode === MODE.ONLYTAG &&
|
||||
obj.origin !== "+delete" &&
|
||||
obj.origin !== "inserttag" &&
|
||||
obj.origin !== "setValue")
|
||||
) {
|
||||
obj.cancel();
|
||||
}
|
||||
|
||||
// 事件内,mode只能从每次props取,不然取不到最新
|
||||
if (
|
||||
props.mode === MODE.FORMULA &&
|
||||
obj.origin !== "+delete" &&
|
||||
obj.origin !== "inserttag" &&
|
||||
obj.origin !== "setValue"
|
||||
) {
|
||||
text = text.map((t: any) =>
|
||||
t
|
||||
.toUpperCase()
|
||||
.split("")
|
||||
.filter((t: any) =>
|
||||
(obj.origin === "paste"
|
||||
? /[0-9A-Z\+\-\*\/\(\)\,\.\$]/
|
||||
: /[0-9A-Z\+\-\*\/\(\)\,\.]/
|
||||
).test(t)
|
||||
)
|
||||
.join("")
|
||||
);
|
||||
}
|
||||
if (
|
||||
props.mode === MODE.DATE &&
|
||||
obj.origin !== "+delete" &&
|
||||
obj.origin !== "inserttag" &&
|
||||
obj.origin !== "setValue"
|
||||
) {
|
||||
text = text.map((t: any) =>
|
||||
t
|
||||
.split("")
|
||||
.filter((t: any) =>
|
||||
(obj.origin === "paste" ? /[0-9YMdhm\+\-\$]/ : /[0-9YMdhm\+\-]/).test(
|
||||
t
|
||||
)
|
||||
)
|
||||
.join("")
|
||||
);
|
||||
}
|
||||
obj.update(obj.from, obj.to, text);
|
||||
};
|
||||
|
||||
//编辑器 foucus
|
||||
const cmFocus = (): void => {
|
||||
emit("onFocus");
|
||||
};
|
||||
|
||||
//编辑器 blur
|
||||
const cmBlur = (): void => {
|
||||
emit("onBlur");
|
||||
};
|
||||
|
||||
//设置 value
|
||||
const setValue = (value: string): void => {
|
||||
// const position = cmInstance.getCursor()
|
||||
cmInstance.setValue(value || "");
|
||||
// const newPosition = {
|
||||
// line: position.line,
|
||||
// ch: position.ch + value.length - 2,
|
||||
// }
|
||||
|
||||
// cmInstance.focus()
|
||||
// cmInstance.setCursor(newPosition)
|
||||
// cmInstance.execCommand('goDocEnd')
|
||||
};
|
||||
const getValue = () => {
|
||||
return cmInstance.getValue();
|
||||
};
|
||||
//回显,更新渲染
|
||||
let markers: any = null;
|
||||
const updateTextareaView = () => {
|
||||
const { mode, operatorsSetMargin } = props;
|
||||
const value = cmInstance.getValue();
|
||||
if (markers) {
|
||||
markers.forEach((marker: any) => marker.clear());
|
||||
}
|
||||
markers = [];
|
||||
markColumns(markers, value);
|
||||
if (mode === MODE.FORMULA || operatorsSetMargin) {
|
||||
markOperators(markers, value);
|
||||
}
|
||||
};
|
||||
|
||||
//光标行
|
||||
const markColumns = (markers: any, value: any) => {
|
||||
const poss = getRePosFromStr(value);
|
||||
poss.forEach((pos: any, i: number) => {
|
||||
renderColumnTag(pos.tag, { isLast: i === poss.length - 1 }, (node: any) => {
|
||||
markers.push(
|
||||
cmInstance.markText(
|
||||
{ line: pos.line, ch: pos.start },
|
||||
{ line: pos.line, ch: pos.stop },
|
||||
{ replacedWith: node, handleMouseEvents: true }
|
||||
)
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
//光标操作
|
||||
const markOperators = (markers: any, value: any) => {
|
||||
const poss = getRePosFromStr(value, /\+|\-|\*|\/|\(|\)|,/g);
|
||||
poss.forEach((pos: any, i: number) => {
|
||||
const operatorEle = document.createElement("span");
|
||||
operatorEle.classList.add("operator");
|
||||
operatorEle.innerHTML = pos.tag;
|
||||
markers.push(
|
||||
cmInstance.markText(
|
||||
{ line: pos.line, ch: pos.start },
|
||||
{ line: pos.line, ch: pos.stop },
|
||||
{ replacedWith: operatorEle, handleMouseEvents: true }
|
||||
)
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
//渲染 tag
|
||||
const renderColumnTag = (id: any, options: any, cb: any) => {
|
||||
const node = document.createElement("div");
|
||||
node.classList.add("columnTagCon");
|
||||
//自定义渲染tag
|
||||
if (props.renderTag) {
|
||||
const tag = props.renderTag(id, options);
|
||||
if (tag instanceof HTMLElement) {
|
||||
node.appendChild(tag);
|
||||
cb(node);
|
||||
return;
|
||||
}
|
||||
}
|
||||
node.append(id);
|
||||
cb(node);
|
||||
return;
|
||||
};
|
||||
|
||||
//插入 tag
|
||||
const insertColumnTag = (id: string) => {
|
||||
const { mode, autoComma } = props;
|
||||
const position = cmInstance.getCursor();
|
||||
const editorValue = cmInstance.getValue();
|
||||
|
||||
cmInstance.replaceRange(
|
||||
`${
|
||||
mode === MODE.FORMULA && autoComma && editorValue[position.ch - 1] === "$"
|
||||
? ","
|
||||
: ""
|
||||
}$${id}$`,
|
||||
position,
|
||||
undefined,
|
||||
"inserttag"
|
||||
);
|
||||
cmInstance.focus();
|
||||
cmInstance.execCommand("goDocEnd");
|
||||
if (cmEle.value) {
|
||||
cmEle.value.scrollTop = cmEle.value.scrollHeight - cmEle.value.clientHeight;
|
||||
}
|
||||
};
|
||||
// 获取光标位置
|
||||
const getCursor = () => {
|
||||
return cmInstance.getCursor();
|
||||
};
|
||||
// 设置光标位置
|
||||
const setCustomCursor = (position: { line: number; ch: number }) => {
|
||||
cmInstance.setCursor(position);
|
||||
};
|
||||
// 插入公式
|
||||
const insertFormula = (value: string) => {
|
||||
const { mode, autoComma } = props;
|
||||
const position = cmInstance.getCursor();
|
||||
|
||||
cmInstance.replaceRange(value, position, undefined, "insertFormula");
|
||||
setTimeout(() => {
|
||||
const newPosition = {
|
||||
line: position.line,
|
||||
ch: position.ch + value.length - 2,
|
||||
};
|
||||
|
||||
cmInstance.focus();
|
||||
cmInstance.setCursor(newPosition);
|
||||
// cmInstance.execCommand('goDocEnd')
|
||||
}, 0);
|
||||
if (cmEle.value) {
|
||||
cmEle.value.scrollTop = cmEle.value.scrollHeight - cmEle.value.clientHeight;
|
||||
}
|
||||
};
|
||||
defineExpose({
|
||||
insertColumnTag,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tagInputarea {
|
||||
min-width: 0;
|
||||
|
||||
:deep {
|
||||
.tagInputareaIuput {
|
||||
overflow: auto;
|
||||
border: 1px solid #409eff;
|
||||
border-radius: 3px;
|
||||
|
||||
&:not(.active) {
|
||||
border-color: #ccc !important;
|
||||
}
|
||||
// &.hasRightIcon {
|
||||
// border-radius: 3px 0 0 3px;
|
||||
// }
|
||||
&.readonlyBg {
|
||||
.CodeMirror {
|
||||
background-color: #eee !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
// .rightIcon {
|
||||
// background-color: #f5f5f5;
|
||||
// font-size: 20px;
|
||||
// color: #757575;
|
||||
// height: 34px;
|
||||
// line-height: 33px;
|
||||
// padding: 0 7px;
|
||||
// border: 1px solid #ccc;
|
||||
// border-left: none;
|
||||
// border-radius: 0 3px 3px 0;
|
||||
// }
|
||||
.CodeMirror {
|
||||
box-sizing: border-box;
|
||||
height: auto !important;
|
||||
font-family: inherit !important;
|
||||
|
||||
.CodeMirror-vscrollbar {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
// .CodeMirror-scroll {
|
||||
// height: auto;
|
||||
// overflow-y: hidden;
|
||||
// overflow-x: auto;
|
||||
// }
|
||||
|
||||
.CodeMirror-lines {
|
||||
min-height: 35px;
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
.CodeMirror-line {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.columnTagCon {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
// box-sizing: border-box;
|
||||
// padding: 2px 4px;
|
||||
// max-width: 100%;
|
||||
// background: #d8eeff;
|
||||
// color: #174c76;
|
||||
// border: 1px solid #bbd6ea;
|
||||
// border-radius: 5px;
|
||||
}
|
||||
|
||||
.columnTag {
|
||||
position: relative;
|
||||
box-sizing: border-box;
|
||||
display: inline-flex;
|
||||
height: 24px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #90caf9;
|
||||
border-radius: 24px;
|
||||
|
||||
.columnName,
|
||||
.columnValue {
|
||||
display: inline-block;
|
||||
height: 22px;
|
||||
padding: 0 10px;
|
||||
overflow: hidden;
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.columnName {
|
||||
color: #2196f3;
|
||||
background-color: rgb(33 150 243 / 6%);
|
||||
}
|
||||
|
||||
.columnValue {
|
||||
color: #fff;
|
||||
background-color: #249eff;
|
||||
border-radius: 0 22px 22px 0;
|
||||
|
||||
.ellipsis {
|
||||
max-width: 9em;
|
||||
}
|
||||
}
|
||||
|
||||
&.onlytag {
|
||||
margin-right: 6px;
|
||||
|
||||
&::after {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: -6px;
|
||||
content: ",";
|
||||
}
|
||||
}
|
||||
|
||||
&.deleted {
|
||||
border-color: #f44336;
|
||||
|
||||
.columnName {
|
||||
color: #f44336;
|
||||
background-color: rgb(244 67 54 / 6%);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #f44336;
|
||||
|
||||
.columnName {
|
||||
color: #f44336;
|
||||
background-color: rgb(244 67 54 / 12%);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
border-color: #ddd;
|
||||
|
||||
.columnName {
|
||||
color: #9e9e9e;
|
||||
background-color: rgb(158 158 158 / 6%);
|
||||
}
|
||||
|
||||
.columnValue {
|
||||
background-color: #bdbdbd;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.operator {
|
||||
margin: 0 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
/**
|
||||
* getRePosFromStr 正则匹配字段返回位置信息
|
||||
* */
|
||||
export function getRePosFromStr(text: any = "", re: any = /\$.+?\$/g) {
|
||||
const lines = text.split("\n");
|
||||
const positions: any = [];
|
||||
let m;
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const l = lines[i];
|
||||
while ((m = re.exec(l)) !== null) {
|
||||
const tag = m[0].substring(1, m[0].length - 1);
|
||||
positions.push({
|
||||
line: i,
|
||||
start: m.index,
|
||||
stop: m.index + m[0].length,
|
||||
tag,
|
||||
});
|
||||
}
|
||||
}
|
||||
return positions;
|
||||
}
|
||||
/**
|
||||
* 输入框模式
|
||||
*/
|
||||
export enum MODE {
|
||||
// 文本
|
||||
TEXT = 1,
|
||||
// 公式
|
||||
FORMULA,
|
||||
// 只允许选择tag
|
||||
ONLYTAG,
|
||||
// 日期
|
||||
DATE,
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ const tagsViewStore = useTagsViewStore();
|
|||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 50px;
|
||||
padding-top: 34px;
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
|
|
@ -42,4 +42,31 @@ const tagsViewStore = useTagsViewStore();
|
|||
padding-top: 84px;
|
||||
}
|
||||
}
|
||||
|
||||
.isMix {
|
||||
.app-main {
|
||||
height: calc(100vh - 50px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.hasTagsView {
|
||||
.app-main {
|
||||
/* 84 = navbar + tags-view = 50 + 34 */
|
||||
height: calc(100vh - 84px);
|
||||
}
|
||||
|
||||
.fixed-header + .app-main {
|
||||
min-height: calc(100vh - 50px);
|
||||
padding-top: 34px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.isTop {
|
||||
.hasTagsView {
|
||||
.fixed-header + .app-main {
|
||||
padding-top: 34px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -5,14 +5,32 @@ import { useAppStore } from "@/store/modules/app";
|
|||
import IconEpSunny from "~icons/ep/sunny";
|
||||
import IconEpMoon from "~icons/ep/moon";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
import defaultSettings from "@/settings";
|
||||
/**
|
||||
* 暗黑模式
|
||||
*/
|
||||
const settingsStore = useSettingsStore();
|
||||
const permissionStore = usePermissionStore();
|
||||
const appStore = useAppStore();
|
||||
|
||||
const isDark = useDark();
|
||||
console.log("isDark1", isDark.value);
|
||||
|
||||
watch(
|
||||
() => defaultSettings.theme,
|
||||
(newVal: string) => {
|
||||
if (
|
||||
(newVal == "dark" && isDark.value == false) ||
|
||||
(newVal == "light" && isDark.value == true)
|
||||
) {
|
||||
toggleDark();
|
||||
console.log("isDark2", isDark.value);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
computed(() => {});
|
||||
|
||||
const toggleDark = () => useToggle(isDark);
|
||||
|
||||
function findOutermostParent(tree: any[], findName: string) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ defineProps({
|
|||
},
|
||||
});
|
||||
|
||||
const layout = computed(() => settingsStore.layout);
|
||||
|
||||
const logo = ref(new URL(`../../../assets/logo.png`, import.meta.url).href);
|
||||
</script>
|
||||
|
||||
|
|
@ -25,9 +27,6 @@ const logo = ref(new URL(`../../../assets/logo.png`, import.meta.url).href);
|
|||
to="/"
|
||||
>
|
||||
<img v-if="settingsStore.sidebarLogo" :src="logo" class="w-5 h-5" />
|
||||
<span v-else class="ml-3 text-white text-sm font-bold"
|
||||
>vue3-element-admin</span
|
||||
>
|
||||
</router-link>
|
||||
|
||||
<router-link
|
||||
|
|
|
|||
|
|
@ -124,23 +124,6 @@ function toggleSideBar() {
|
|||
}
|
||||
}
|
||||
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$sideBarWidth});
|
||||
transition: width 0.28s;
|
||||
}
|
||||
|
||||
.hideSidebar .fixed-header {
|
||||
width: calc(100% - 54px);
|
||||
}
|
||||
|
||||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.drawer-bg {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
|
|
@ -231,8 +214,11 @@ function toggleSideBar() {
|
|||
|
||||
.openSidebar {
|
||||
.mix-wrap {
|
||||
.el-menu {
|
||||
.left-wrap {
|
||||
width: $sideBarWidth;
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,3 +55,33 @@ watchEffect(() => {
|
|||
</RightPanel>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
width: calc(100% - #{$sideBarWidth});
|
||||
transition: width 0.28s;
|
||||
}
|
||||
|
||||
.hideSidebar .fixed-header {
|
||||
width: calc(100% - 54px);
|
||||
}
|
||||
|
||||
.isTop .fixed-header {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.mobile .fixed-header {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.isMix,
|
||||
.isTop {
|
||||
.fixed-header {
|
||||
top: 50px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// 系统设置
|
||||
/**
|
||||
* 系统设置
|
||||
*/
|
||||
interface DefaultSettings {
|
||||
/**
|
||||
* 系统title
|
||||
*/
|
||||
title: string;
|
||||
|
||||
/**
|
||||
* 是否显示设置
|
||||
*/
|
||||
|
|
@ -22,21 +23,21 @@ interface DefaultSettings {
|
|||
*/
|
||||
sidebarLogo: boolean;
|
||||
/**
|
||||
* 导航栏布局
|
||||
* 导航栏布局(left|top|mix)
|
||||
*/
|
||||
layout: string;
|
||||
/**
|
||||
* 主题模式
|
||||
* 主题模式(dark|light)
|
||||
*/
|
||||
theme: string;
|
||||
|
||||
/**
|
||||
* 布局大小
|
||||
* 布局大小(default |large |small)
|
||||
*/
|
||||
size: string;
|
||||
|
||||
/**
|
||||
* 语言
|
||||
* 语言( zh-cn| en)
|
||||
*/
|
||||
language: string;
|
||||
}
|
||||
|
|
@ -47,16 +48,10 @@ const defaultSettings: DefaultSettings = {
|
|||
tagsView: true,
|
||||
fixedHeader: false,
|
||||
sidebarLogo: true,
|
||||
layout: "mix",
|
||||
/**
|
||||
* 主题模式
|
||||
*
|
||||
* dark:暗黑模式
|
||||
* light: 明亮模式
|
||||
*/
|
||||
theme: "dark",
|
||||
size: "default", // default |large |small
|
||||
language: "zh-cn", // zh-cn| en
|
||||
layout: "mix", // 默认混合模式
|
||||
theme: "dark", // 默认暗黑模式
|
||||
size: "default",
|
||||
language: "zh-cn",
|
||||
};
|
||||
|
||||
export default defaultSettings;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,18 @@
|
|||
import { defineStore } from "pinia";
|
||||
import defaultSettings from "@/settings";
|
||||
import { useStorage } from "@vueuse/core";
|
||||
|
||||
export const useSettingsStore = defineStore("setting", () => {
|
||||
// state
|
||||
const tagsView = useStorage<boolean>("tagsView", defaultSettings.tagsView);
|
||||
|
||||
const showSettings = ref<boolean>(defaultSettings.showSettings);
|
||||
const fixedHeader = ref<boolean>(defaultSettings.fixedHeader);
|
||||
const sidebarLogo = ref<boolean>(defaultSettings.sidebarLogo);
|
||||
|
||||
const fixedHeader = useStorage<boolean>(
|
||||
"fixedHeader",
|
||||
defaultSettings.fixedHeader
|
||||
);
|
||||
|
||||
const layout = useStorage<string>("layout", defaultSettings.layout);
|
||||
|
||||
// actions
|
||||
|
|
@ -31,8 +34,6 @@ export const useSettingsStore = defineStore("setting", () => {
|
|||
case "layout":
|
||||
layout.value = value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -83,14 +83,11 @@
|
|||
}
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.left-wrap {
|
||||
.svg-icon {
|
||||
margin-top: -2px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
width: 54px;
|
||||
}
|
||||
|
||||
.hideSidebar {
|
||||
.sidebar-container {
|
||||
width: 54px !important;
|
||||
|
||||
|
|
@ -100,7 +97,7 @@
|
|||
|
||||
.header {
|
||||
.logo-wrap {
|
||||
width: 63px !important;
|
||||
width: 54px !important;
|
||||
transition: transform 0.28s;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,105 +0,0 @@
|
|||
<script lang="ts" setup>
|
||||
import TagTextInput from "@/components/TagInput/index.vue";
|
||||
|
||||
//假数据
|
||||
let arrName = new Array(20).fill("tag");
|
||||
|
||||
/**
|
||||
* renderTag 渲染 tag
|
||||
* @param {any} id $$匹配到的之间的值
|
||||
* @param {object} options 一些参数
|
||||
* @return {element} 返回一个dom节点
|
||||
* */
|
||||
const renderTag = (id: any, options: any) => {
|
||||
//拼接tag名称
|
||||
let tagName = `${arrName[Number(id)]}-${id}`;
|
||||
//创建tag元素
|
||||
const ele = document.createElement("div");
|
||||
ele.classList.add("tag-demo-con");
|
||||
ele.innerHTML = `<div class="tag-wrap"><div class="tag">${tagName}</div></div>`;
|
||||
return ele;
|
||||
};
|
||||
|
||||
// 触发增加 tag
|
||||
const refTagTextarea = ref();
|
||||
const addTag = (id: any) => {
|
||||
refTagTextarea.value.insertColumnTag(id);
|
||||
};
|
||||
|
||||
/**
|
||||
* 编辑器 change 监听
|
||||
* @param {any} error 默认为 null
|
||||
* @param {string} value 返回编辑器内容
|
||||
* @param {object} obj 返回一个编辑事件描述对象
|
||||
* */
|
||||
const onChange = (error: any, value: string, obj: any): void => {
|
||||
console.log("onChange", error, value, obj);
|
||||
};
|
||||
|
||||
const onFocus = (): void => {
|
||||
console.log("onFocus");
|
||||
};
|
||||
|
||||
const onBlur = (): void => {
|
||||
console.log("onBlur");
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<div class="demo-tagInput">
|
||||
<TagTextInput
|
||||
ref="refTagTextarea"
|
||||
class-name="demo"
|
||||
:default-value="`April$18$`"
|
||||
:readonly="false"
|
||||
:no-cursor="false"
|
||||
:min-height="200"
|
||||
:render-tag="renderTag"
|
||||
@on-change="onChange"
|
||||
@on-focus="onFocus"
|
||||
@on-blur="onBlur"
|
||||
/>
|
||||
|
||||
<div class="tag-wrap">
|
||||
<el-button v-for="item in 5" :key="item" @click="addTag(item)">
|
||||
{{ item }}</el-button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style lang="scss" scoped>
|
||||
.demo-tagInput {
|
||||
width: 500px;
|
||||
height: auto;
|
||||
margin: 10px;
|
||||
|
||||
:deep {
|
||||
.tag-demo-con {
|
||||
.tag-wrap {
|
||||
height: 25px;
|
||||
line-height: 25px;
|
||||
|
||||
.tag {
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
padding: 2px 8px;
|
||||
margin: 0 4px;
|
||||
color: #174c76;
|
||||
background: #d8eeff;
|
||||
border: 1px solid #bbd6ea;
|
||||
border-radius: 16px;
|
||||
}
|
||||
// padding: 4px;
|
||||
// margin: 4px;
|
||||
// border: 1px solid #ccc;
|
||||
// border-radius: 5px;
|
||||
// line-height: 1em;
|
||||
// min-width: 25px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tag-wrap {
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -4,7 +4,9 @@ import { sendToAll, sendToUser } from "@/api/websocket"; // 点对点消息列
|
|||
|
||||
import { useUserStore } from "@/store/modules/user";
|
||||
|
||||
import { useWebSocket } from "@vueuse/core";
|
||||
import SockJS from "sockjs-client"; // 报错 global is not defined 换成下面的引入
|
||||
// import SockJS from "sockjs-client/dist/sockjs.min.js";
|
||||
import Stomp from "stompjs";
|
||||
|
||||
const inputVal = ref("初始内容");
|
||||
|
||||
|
|
@ -13,58 +15,6 @@ const p2pMsgs = ref<string[]>(["接收到一条点对线消息"]);
|
|||
|
||||
const userId = useUserStore().userId;
|
||||
|
||||
const { data, status, close, send, open } = useWebSocket(
|
||||
"ws://localhost:8989/ws",
|
||||
{
|
||||
onConnected(ws) {
|
||||
console.log("订阅主题");
|
||||
// 连接建立后发送订阅消息
|
||||
ws.send(JSON.stringify({ type: "subscribe", topic: "/topic/all" }));
|
||||
},
|
||||
onMessage(ws, event) {
|
||||
// 获取接收到的消息内容
|
||||
const message = event.data;
|
||||
|
||||
// 处理消息内容
|
||||
console.log("Received message:", message);
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
// 监听 WebSocket 连接状态变化
|
||||
watch(status, (newStatus) => {
|
||||
if (newStatus === "OPEN") {
|
||||
// 连接已打开,订阅主题
|
||||
console.log(" 连接已打开,订阅主题");
|
||||
const subscribeMessage = {
|
||||
type: "subscribe",
|
||||
channel: "/topic/all",
|
||||
};
|
||||
send(JSON.stringify(subscribeMessage));
|
||||
} else if (newStatus === "CLOSED") {
|
||||
// 连接已关闭,执行相应的清理操作
|
||||
// ...
|
||||
}
|
||||
});
|
||||
|
||||
// 监听 WebSocket 接收到的消息
|
||||
watch(data, (newData) => {
|
||||
console.log("Received data:", newData);
|
||||
|
||||
// 解析消息体
|
||||
const message = JSON.parse(newData);
|
||||
|
||||
// 判断消息主题并处理
|
||||
if (message.topic === "topic1") {
|
||||
// 处理来自 topic1 的消息
|
||||
console.log("Received message from topic1:", message);
|
||||
} else if (message.topic === "topic2") {
|
||||
// 处理来自 topic2 的消息
|
||||
console.log("Received message from topic2:", message);
|
||||
}
|
||||
// 可以根据需要添加更多的判断逻辑来处理其他主题的消息
|
||||
});
|
||||
|
||||
function handleSendToAll() {
|
||||
sendToAll(inputVal.value);
|
||||
}
|
||||
|
|
@ -72,7 +22,24 @@ function handleSendToAll() {
|
|||
function handleSendToUser() {
|
||||
sendToUser(userId, inputVal.value);
|
||||
}
|
||||
onMounted(() => {});
|
||||
|
||||
let stompClient: Stomp.Client;
|
||||
|
||||
function initWebSocket() {
|
||||
stompClient = Stomp.overWS("ws://localhost:8989/ws");
|
||||
|
||||
stompClient.connect({}, () => {
|
||||
console.log("连接成功");
|
||||
|
||||
stompClient.subscribe("/topic/all", (res) => {
|
||||
console.log("订阅响应");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initWebSocket();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
|||
|
|
@ -12,15 +12,12 @@ import IconsResolver from "unplugin-icons/resolver";
|
|||
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
|
||||
|
||||
import { viteMockServe } from "vite-plugin-mock";
|
||||
import visualizer from "rollup-plugin-visualizer";
|
||||
|
||||
import UnoCSS from "unocss/vite";
|
||||
import path from "path";
|
||||
|
||||
import viteCompression from "vite-plugin-compression";
|
||||
|
||||
const pathSrc = path.resolve(__dirname, "src");
|
||||
|
||||
/** 参考Vite官方配置: https://cn.vitejs.dev/config */
|
||||
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
||||
const env = loadEnv(mode, process.cwd());
|
||||
|
||||
|
|
@ -102,35 +99,17 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||
// 自动安装图标库
|
||||
autoInstall: true,
|
||||
}),
|
||||
|
||||
createSvgIconsPlugin({
|
||||
// 指定需要缓存的图标文件夹
|
||||
iconDirs: [path.resolve(pathSrc, "assets/icons")],
|
||||
// 指定symbolId格式
|
||||
symbolId: "icon-[dir]-[name]",
|
||||
}),
|
||||
// 代码压缩
|
||||
viteCompression({
|
||||
verbose: true, // 默认即可
|
||||
disable: true, // 是否禁用压缩,默认禁用,true为禁用,false为开启,打开压缩需配置nginx支持
|
||||
deleteOriginFile: true, // 删除源文件
|
||||
threshold: 10240, // 压缩前最小文件大小
|
||||
algorithm: "gzip", // 压缩算法
|
||||
ext: ".gz", // 文件类型
|
||||
}),
|
||||
|
||||
// https://github.com/anncwb/vite-plugin-mock/issues/9
|
||||
viteMockServe({
|
||||
ignore: /^\_/,
|
||||
mockPath: "mock",
|
||||
enable: mode === "development",
|
||||
// https://github.com/anncwb/vite-plugin-mock/issues/9
|
||||
}),
|
||||
|
||||
visualizer({
|
||||
filename: "./stats.html",
|
||||
open: false,
|
||||
gzipSize: true,
|
||||
brotliSize: true,
|
||||
}),
|
||||
],
|
||||
// 预加载项目必需的组件
|
||||
|
|
@ -192,7 +171,6 @@ export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
|
|||
"@wangeditor/editor",
|
||||
"@wangeditor/editor-for-vue",
|
||||
"vue-i18n",
|
||||
"codemirror",
|
||||
],
|
||||
},
|
||||
// 构建
|
||||
|
|
|
|||
Loading…
Reference in New Issue