Merge remote-tracking branch 'origin/main' into gitee

This commit is contained in:
xiaoxian521
2024-01-02 19:52:43 +08:00
162 changed files with 4345 additions and 4437 deletions

View File

@@ -7,15 +7,8 @@ defineOptions({
name: "ReAnimateSelector"
});
const props = defineProps({
modelValue: {
require: false,
type: String
}
});
const emit = defineEmits<{ (e: "update:modelValue", v: string) }>();
const inputValue = defineModel({ type: String });
const inputValue = toRef(props, "modelValue");
const animatesList = ref(animates);
const copyAnimatesList = cloneDeep(animatesList);
@@ -47,11 +40,11 @@ const animateStyle = computed(
);
function onChangeIcon(animate: string) {
emit("update:modelValue", animate);
inputValue.value = animate;
}
function onClear() {
emit("update:modelValue", "");
inputValue.value = "";
}
function filterMethod(value: any) {
@@ -81,8 +74,8 @@ function onMouseleave() {
placeholder="请选择动画"
clearable
filterable
@clear="onClear"
:filter-method="filterMethod"
@clear="onClear"
>
<template #empty>
<div class="w-[280px]">

View File

@@ -1,4 +1,4 @@
import { PropType } from "vue";
import type { PropType } from "vue";
import propTypes from "@/utils/propTypes";
export const countToProps = {
startVal: propTypes.number.def(0),

View File

@@ -1,4 +1,4 @@
import { PropType } from "vue";
import type { PropType } from "vue";
import propTypes from "@/utils/propTypes";
export const reboundProps = {
delay: propTypes.number.def(1),

View File

@@ -1,7 +1,5 @@
@import "cropperjs/dist/cropper.css";
@import "tippy.js/dist/tippy.css";
@import "tippy.js/themes/light.css";
@import "tippy.js/animations/perspective.css";
@import "cropperjs/dist/cropper.css";
.re-circled {
.cropper-view-box,

View File

@@ -1,16 +1,16 @@
import "./circled.css";
import Cropper from "cropperjs";
import { useTippy } from "vue-tippy";
import { ElUpload } from "element-plus";
import type { CSSProperties } from "vue";
import { useResizeObserver } from "@vueuse/core";
import { longpress } from "@/directives/longpress";
import { useTippy, directive as tippy } from "vue-tippy";
import { delay, debounce, isArray, downloadByBase64 } from "@pureadmin/utils";
import {
ref,
unref,
computed,
PropType,
type PropType,
onMounted,
onUnmounted,
defineComponent
@@ -233,7 +233,6 @@ export default defineComponent({
const menuContent = defineComponent({
directives: {
tippy,
longpress
},
setup() {

View File

@@ -27,8 +27,11 @@ const addDialog = (options: DialogOptions) => {
/** 关闭弹框 */
const closeDialog = (options: DialogOptions, index: number, args?: any) => {
dialogStore.value.splice(index, 1);
dialogStore.value[index].visible = false;
options.closeCallBack && options.closeCallBack({ options, index, args });
useTimeoutFn(() => {
dialogStore.value.splice(index, 1);
}, 200);
};
/**

View File

@@ -84,11 +84,11 @@ function handleClose(
<template>
<el-dialog
class="pure-dialog"
v-for="(options, index) in dialogStore"
:key="index"
v-bind="options"
v-model="options.visible"
class="pure-dialog"
:fullscreen="fullscreen ? true : options?.fullscreen ? true : false"
@close="handleClose(options, index)"
@opened="eventsCallBack('open', options, index)"
@@ -116,15 +116,15 @@ function handleClose(
options?.fullscreen
? ExitFullscreen
: fullscreen
? ExitFullscreen
: Fullscreen
? ExitFullscreen
: Fullscreen
"
/>
</i>
</div>
<component
v-else
:is="options?.headerRenderer({ close, titleId, titleClass })"
v-else
/>
</template>
<component

View File

@@ -1,5 +1,5 @@
import "./index.css";
import { h, defineComponent, Component } from "vue";
import { h, defineComponent, type Component } from "vue";
export interface attrsType {
width?: string;

View File

@@ -18,7 +18,6 @@ export default defineComponent({
name: "ReFlop",
props,
setup(props) {
// eslint-disable-next-line vue/no-setup-props-destructure
const { frontText, backText, duration } = props;
const isFlipping = ref(false);
const flipType = ref("down");

View File

@@ -35,9 +35,9 @@ const nodeDragNode = item => {
<!-- 左侧bpmn元素选择器 -->
<div class="node-panel">
<div
class="node-item dark:text-bg_color"
v-for="item in props.nodeList"
:key="item.text"
class="node-item dark:text-bg_color"
@mousedown="nodeDragNode(item)"
>
<div class="node-item-icon" :class="item.class">

View File

@@ -10,15 +10,8 @@ defineOptions({
name: "IconSelect"
});
const props = defineProps({
modelValue: {
require: false,
type: String
}
});
const emit = defineEmits<{ (e: "update:modelValue", v: string) }>();
const inputValue = defineModel({ type: String });
const inputValue = toRef(props, "modelValue");
const iconList = ref(IconJson);
const icon = ref();
const currentActiveType = ref("ep:");
@@ -68,11 +61,11 @@ const iconItemStyle = computed((): ParameterCSSProperties => {
});
function setVal() {
currentActiveType.value = props.modelValue.substring(
currentActiveType.value = inputValue.value.substring(
0,
props.modelValue.indexOf(":") + 1
inputValue.value.indexOf(":") + 1
);
icon.value = props.modelValue.substring(props.modelValue.indexOf(":") + 1);
icon.value = inputValue.value.substring(inputValue.value.indexOf(":") + 1);
}
function onBeforeEnter() {
@@ -96,7 +89,7 @@ function handleClick({ props }) {
function onChangeIcon(item) {
icon.value = item;
emit("update:modelValue", currentActiveType.value + item);
inputValue.value = currentActiveType.value + item;
}
function onCurrentChange(page) {
@@ -105,7 +98,7 @@ function onCurrentChange(page) {
function onClear() {
icon.value = "";
emit("update:modelValue", "");
inputValue.value = "";
}
watch(
@@ -117,7 +110,7 @@ watch(
{ immediate: true }
);
watch(
() => props.modelValue,
() => inputValue.value,
val => val && setVal(),
{ immediate: true }
);
@@ -151,8 +144,8 @@ watch(
</template>
<el-input
class="px-2 pt-2"
v-model="filterValue"
class="px-2 pt-2"
placeholder="搜索图标"
clearable
/>

View File

@@ -1,5 +1,5 @@
import { iconType } from "./types";
import { h, defineComponent, Component } from "vue";
import type { iconType } from "./types";
import { h, defineComponent, type Component } from "vue";
import { IconifyIconOnline, IconifyIconOffline, FontIcon } from "../index";
/**
@@ -33,7 +33,7 @@ export function useRenderIcon(icon: any, attrs?: iconType): Component {
});
} else if (typeof icon === "function" || typeof icon?.render === "function") {
// svg
return icon;
return attrs ? h(icon, { ...attrs }) : icon;
} else if (typeof icon === "object") {
return defineComponent({
name: "OfflineIcon",

View File

@@ -13,7 +13,8 @@ export interface iconType {
align?: string;
onLoad?: Function;
includes?: Function;
// all icon
// svg 需要什么SVG属性自行添加
fill?: string;
// all icon
style?: object;
}

View File

@@ -4,13 +4,13 @@ import {
watch,
nextTick,
computed,
PropType,
type PropType,
defineComponent
} from "vue";
import "./index.scss";
import propTypes from "@/utils/propTypes";
import { isString, cloneDeep } from "@pureadmin/utils";
import QRCode, { QRCodeRenderersOptions } from "qrcode";
import QRCode, { type QRCodeRenderersOptions } from "qrcode";
import RefreshRight from "@iconify-icons/ep/refresh-right";
interface QrcodeLogo {

View File

@@ -498,16 +498,16 @@ defineExpose({
<template>
<div :ref="'wrap' + props.classOption['key']">
<div
:style="leftSwitch"
v-if="navigation"
:style="leftSwitch"
:class="leftSwitchClass"
@click="leftSwitchClick"
>
<slot name="left-switch" />
</div>
<div
:style="rightSwitch"
v-if="navigation"
:style="rightSwitch"
:class="rightSwitchClass"
@click="rightSwitchClick"
>
@@ -526,7 +526,7 @@ defineExpose({
<div :ref="'slotList' + props.classOption['key']" :style="float">
<slot />
</div>
<div v-html="copyHtml" :style="float" />
<div :style="float" v-html="copyHtml" />
</div>
</div>
</template>

View File

@@ -6,7 +6,6 @@
color: rgba(0, 0, 0, 0.65);
background-color: rgb(0 0 0 / 4%);
border-radius: 2px;
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.pure-segmented-group {
@@ -43,7 +42,7 @@
text-align: center;
cursor: pointer;
border-radius: 4px;
transition: all 0.2s cubic-bezier(0.645, 0.045, 0.355, 1);
transition: all 0.1s cubic-bezier(0.645, 0.045, 0.355, 1);
}
.pure-segmented-item > div {

View File

@@ -121,7 +121,11 @@ export default defineComponent({
class="pure-segmented-item-icon"
style={{ marginRight: option.label ? "6px" : 0 }}
>
{h(useRenderIcon(option.icon))}
{h(
useRenderIcon(option.icon, {
...option?.iconAttrs
})
)}
</span>
) : null}
{option.label ? (

View File

@@ -1,4 +1,5 @@
import type { VNode, Component } from "vue";
import type { iconType } from "@/components/ReIcon/src/types.ts";
export interface OptionsType {
/** 文字 */
@@ -8,6 +9,8 @@ export interface OptionsType {
* @see {@link 用法参考 https://yiming_chang.gitee.io/pure-admin-doc/pages/icon/#%E9%80%9A%E7%94%A8%E5%9B%BE%E6%A0%87-userendericon-hooks }
*/
icon?: string | Component;
/** 图标属性、样式配置 */
iconAttrs?: iconType;
/** 值 */
value?: string | number;
/** 是否禁用 */

View File

@@ -55,7 +55,6 @@ export default defineComponent({
emits: ["selectedVal"],
setup(props, { emit }) {
const instance = getCurrentInstance();
// eslint-disable-next-line vue/no-setup-props-destructure
const currentValue = props.value;
const rateDisabled = computed(() => {

View File

@@ -1,4 +1,4 @@
import { defineComponent, ref, unref, computed, PropType } from "vue";
import { defineComponent, ref, unref, computed, type PropType } from "vue";
import resizer from "./resizer";
import "./index.css";

View File

@@ -1,4 +1,4 @@
import { App } from "vue";
import type { App } from "vue";
import axios from "axios";
let config: object = {};

View File

@@ -50,6 +50,12 @@ const getSectionStyle = computed(() => {
});
const transitionMain = defineComponent({
props: {
route: {
type: undefined,
required: true
}
},
render() {
const transitionName =
transitions.value(this.route)?.name || "fade-transform";
@@ -72,12 +78,6 @@ const transitionMain = defineComponent({
default: () => [this.$slots.default()]
}
);
},
props: {
route: {
type: undefined,
required: true
}
}
});
</script>
@@ -92,7 +92,8 @@ const transitionMain = defineComponent({
<el-scrollbar
v-if="props.fixedHeader"
:wrap-style="{
display: 'flex'
display: 'flex',
'flex-wrap': 'wrap'
}"
:view-style="{
display: 'flex',
@@ -117,8 +118,8 @@ const transitionMain = defineComponent({
/>
</keep-alive>
<component
v-else
:is="Component"
v-else
:key="route.fullPath"
class="main-content"
/>
@@ -139,8 +140,8 @@ const transitionMain = defineComponent({
/>
</keep-alive>
<component
v-else
:is="Component"
v-else
:key="route.fullPath"
class="main-content"
/>

View File

@@ -62,8 +62,8 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
@click="translationCh"
>
<IconifyIconOffline
class="check-zh"
v-show="locale === 'zh'"
class="check-zh"
:icon="Check"
/>
简体中文
@@ -73,7 +73,7 @@ const { t, locale, translationCh, translationEn } = useTranslationLang();
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn"
>
<span class="check-en" v-show="locale === 'en'">
<span v-show="locale === 'en'" class="check-en">
<IconifyIconOffline :icon="Check" />
</span>
English

View File

@@ -23,8 +23,8 @@ notices.value.map(v => (noticesNum.value += v.list.length));
<template #dropdown>
<el-dropdown-menu>
<el-tabs
:stretch="true"
v-model="activeKey"
:stretch="true"
class="dropdown-tabs"
:style="{ width: notices.length === 0 ? '200px' : '330px' }"
>

View File

@@ -15,8 +15,8 @@ const props = defineProps({
<div v-if="props.list.length">
<NoticeItem
v-for="(item, index) in props.list"
:noticeItem="item"
:key="index"
:noticeItem="item"
/>
</div>
<el-empty v-else description="暂无数据" />

View File

@@ -2,6 +2,7 @@
import { emitter } from "@/utils/mitt";
import { onClickOutside } from "@vueuse/core";
import { ref, computed, onMounted, onBeforeUnmount } from "vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import Close from "@iconify-icons/ep/close";
const target = ref(null);
@@ -9,7 +10,6 @@ const show = ref<Boolean>(false);
const iconClass = computed(() => {
return [
"mr-[20px]",
"outline-none",
"width-[20px]",
"height-[20px]",
@@ -22,6 +22,8 @@ const iconClass = computed(() => {
];
});
const { onReset } = useDataThemeChange();
onClickOutside(target, (event: any) => {
if (event.clientX > target.value.offsetLeft) return;
show.value = false;
@@ -43,23 +45,47 @@ onBeforeUnmount(() => {
<div :class="{ show: show }" class="right-panel-container">
<div class="right-panel-background" />
<div ref="target" class="right-panel bg-bg_color">
<div class="right-panel-items">
<div class="project-configuration">
<h4 class="dark:text-white">项目配置</h4>
<span title="关闭配置" :class="iconClass">
<IconifyIconOffline
class="dark:text-white"
width="20px"
height="20px"
:icon="Close"
@click="show = !show"
/>
</span>
</div>
<div
class="border-b-[1px] border-solid border-[#dcdfe6] dark:border-[#303030]"
/>
<div
class="project-configuration border-b-[1px] border-solid border-[var(--pure-border-color)]"
>
<h4 class="dark:text-white">项目配置</h4>
<span
v-tippy="{
content: '关闭配置',
placement: 'bottom-start',
zIndex: 41000
}"
:class="iconClass"
>
<IconifyIconOffline
class="dark:text-white"
width="20px"
height="20px"
:icon="Close"
@click="show = !show"
/>
</span>
</div>
<el-scrollbar>
<slot />
</el-scrollbar>
<div
class="flex justify-end p-3 border-t-[1px] border-solid border-[var(--pure-border-color)]"
>
<el-button
v-tippy="{
content: '清空缓存并返回登录页',
placement: 'left-start',
zIndex: 41000
}"
type="danger"
text
bg
@click="onReset"
>
清空缓存
</el-button>
</div>
</div>
</div>
@@ -74,6 +100,10 @@ onBeforeUnmount(() => {
</style>
<style lang="scss" scoped>
:deep(.el-scrollbar) {
height: calc(100vh - 110px);
}
.right-panel-background {
position: fixed;
top: 0;
@@ -90,7 +120,7 @@ onBeforeUnmount(() => {
right: 0;
z-index: 40000;
width: 100%;
max-width: 315px;
max-width: 280px;
height: 100vh;
box-shadow: 0 0 15px 0 rgb(0 0 0 / 5%);
transition: all 0.25s cubic-bezier(0.7, 0.3, 0.1, 1);
@@ -112,47 +142,10 @@ onBeforeUnmount(() => {
}
}
.handle-button {
position: absolute;
top: 45%;
left: -48px;
z-index: 0;
width: 48px;
height: 48px;
font-size: 24px;
line-height: 48px;
color: #fff;
text-align: center;
pointer-events: auto;
cursor: pointer;
background: rgb(24 144 255);
border-radius: 6px 0 0 6px !important;
i {
font-size: 24px;
line-height: 48px;
}
}
.right-panel-items {
height: calc(100vh - 60px);
margin-top: 60px;
overflow-y: auto;
}
.project-configuration {
position: fixed;
top: 15px;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 30px;
margin-left: 10px;
}
:deep(.el-divider--horizontal) {
width: 90%;
margin: 20px auto 0;
padding: 14px 20px;
}
</style>

View File

@@ -146,9 +146,9 @@ onKeyStroke("ArrowDown", handleDown);
<template>
<el-dialog
v-model="show"
top="5vh"
class="pure-search-dialog"
v-model="show"
:show-close="false"
:width="device === 'mobile' ? '80vw' : '40vw'"
:before-close="handleClose"
@@ -161,8 +161,8 @@ onKeyStroke("ArrowDown", handleDown);
>
<el-input
ref="inputRef"
size="large"
v-model="keyword"
size="large"
clearable
placeholder="搜索菜单(中文模式下支持拼音搜索)"
@input="handleSearch"

View File

@@ -8,28 +8,22 @@ import {
nextTick,
onBeforeMount
} from "vue";
import { getConfig } from "@/config";
import { useRouter } from "vue-router";
import panel from "../panel/index.vue";
import { emitter } from "@/utils/mitt";
import { resetRouter } from "@/router";
import { removeToken } from "@/utils/auth";
import { routerArrays } from "@/layout/types";
import { useNav } from "@/layout/hooks/useNav";
import { useAppStoreHook } from "@/store/modules/app";
import { useDark, debounce, useGlobal } from "@pureadmin/utils";
import { toggleTheme } from "@pureadmin/theme/dist/browser-utils";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import Segmented, { type OptionsType } from "@/components/ReSegmented";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import { useDark, debounce, useGlobal, storageLocal } from "@pureadmin/utils";
import Check from "@iconify-icons/ep/check";
import dayIcon from "@/assets/svg/day.svg?component";
import darkIcon from "@/assets/svg/dark.svg?component";
import Check from "@iconify-icons/ep/check";
import Logout from "@iconify-icons/ri/logout-circle-r-line";
const router = useRouter();
const { device } = useNav();
const { isDark } = useDark();
const { device, tooltipEffect } = useNav();
const { $storage } = useGlobal<GlobalPropertiesApi>();
const mixRef = ref();
@@ -40,8 +34,8 @@ const {
dataTheme,
layoutTheme,
themeColors,
toggleClass,
dataThemeChange,
setEpThemeColor,
setLayoutThemeColor
} = useDataThemeChange();
@@ -89,13 +83,6 @@ function storageConfigureChange<T>(key: string, val: T): void {
$storage.configure = storageConfigure;
}
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
const targetEl = target || document.body;
let { className } = targetEl;
className = className.replace(clsName, "").trim();
targetEl.className = flag ? `${className} ${clsName} ` : className;
}
/** 灰色模式设置 */
const greyChange = (value): void => {
toggleClass(settings.greyVal, "html-grey", document.querySelector("html"));
@@ -132,24 +119,11 @@ const multiTagsCacheChange = () => {
useMultiTagsStoreHook().multiTagsCacheChange(multiTagsCache);
};
/** 清空缓存并返回登录页 */
function onReset() {
removeToken();
storageLocal().clear();
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout);
setEpThemeColor(EpThemeColor);
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
toggleClass(Grey, "html-grey", document.querySelector("html"));
toggleClass(Weak, "html-weakness", document.querySelector("html"));
router.push("/login");
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
resetRouter();
}
function onChange(label) {
storageConfigureChange("showModel", label);
emitter.emit("tagViewsShowModel", label);
function onChange({ option }) {
const { value } = option;
markValue.value = value;
storageConfigureChange("showModel", value);
emitter.emit("tagViewsShowModel", value);
}
/** 侧边栏Logo */
@@ -185,6 +159,32 @@ const getThemeColor = computed(() => {
};
});
const themeOptions = computed<Array<OptionsType>>(() => {
return [
{
label: "亮色",
icon: dayIcon,
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
},
{
label: "暗色",
icon: darkIcon,
iconAttrs: { fill: isDark.value ? "#fff" : "#000" }
}
];
});
const markOptions: Array<OptionsType> = [
{
label: "灵动",
value: "smart"
},
{
label: "卡片",
value: "card"
}
];
/** 设置导航模式 */
function setLayoutModel(layout: string) {
layoutTheme.value.layout = layout;
@@ -194,7 +194,8 @@ function setLayoutModel(layout: string) {
theme: layoutTheme.value.theme,
darkMode: $storage.layout?.darkMode,
sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: $storage.layout?.epThemeColor
epThemeColor: $storage.layout?.epThemeColor,
themeColor: layoutTheme.value.theme
};
useAppStoreHook().setLayout(layout);
}
@@ -234,185 +235,153 @@ onBeforeMount(() => {
<template>
<panel>
<el-divider>主题</el-divider>
<el-switch
v-model="dataTheme"
inline-prompt
class="pure-datatheme"
:active-icon="dayIcon"
:inactive-icon="darkIcon"
@change="dataThemeChange"
/>
<div class="p-6">
<p class="mb-3 font-medium text-sm dark:text-white">整体风格</p>
<Segmented
:modelValue="dataTheme ? 1 : 0"
:options="themeOptions"
@change="
{
dataTheme = !dataTheme;
dataThemeChange();
}
"
/>
<el-divider>导航栏模式</el-divider>
<ul class="pure-theme">
<el-tooltip
:effect="tooltipEffect"
class="item"
content="左侧模式"
placement="bottom"
popper-class="pure-tooltip"
>
<p class="mt-5 mb-3 font-medium text-sm dark:text-white">主题色</p>
<ul class="theme-color">
<li
v-for="(item, index) in themeColors"
v-show="showThemeColors(item.themeColor)"
:key="index"
:style="getThemeColorStyle(item.color)"
@click="setLayoutThemeColor(item.themeColor)"
>
<el-icon
style="margin: 0.1em 0.1em 0 0"
:size="17"
:color="getThemeColor(item.themeColor)"
>
<IconifyIconOffline :icon="Check" />
</el-icon>
</li>
</ul>
<p class="mt-5 mb-3 font-medium text-sm dark:text-white">导航模式</p>
<ul class="pure-theme">
<li
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
ref="verticalRef"
v-tippy="{
content: '左侧菜单',
zIndex: 41000
}"
:class="layoutTheme.layout === 'vertical' ? 'is-select' : ''"
@click="setLayoutModel('vertical')"
>
<div />
<div />
</li>
</el-tooltip>
<el-tooltip
v-if="device !== 'mobile'"
:effect="tooltipEffect"
class="item"
content="顶部模式"
placement="bottom"
popper-class="pure-tooltip"
>
<li
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
v-if="device !== 'mobile'"
ref="horizontalRef"
v-tippy="{
content: '顶部菜单',
zIndex: 41000
}"
:class="layoutTheme.layout === 'horizontal' ? 'is-select' : ''"
@click="setLayoutModel('horizontal')"
>
<div />
<div />
</li>
</el-tooltip>
<el-tooltip
v-if="device !== 'mobile'"
:effect="tooltipEffect"
class="item"
content="混合模式"
placement="bottom"
popper-class="pure-tooltip"
>
<li
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
v-if="device !== 'mobile'"
ref="mixRef"
v-tippy="{
content: '混合菜单',
zIndex: 41000
}"
:class="layoutTheme.layout === 'mix' ? 'is-select' : ''"
@click="setLayoutModel('mix')"
>
<div />
<div />
</li>
</el-tooltip>
</ul>
</ul>
<el-divider>主题色</el-divider>
<ul class="theme-color">
<li
v-for="(item, index) in themeColors"
:key="index"
v-show="showThemeColors(item.themeColor)"
:style="getThemeColorStyle(item.color)"
@click="setLayoutThemeColor(item.themeColor)"
>
<el-icon
style="margin: 0.1em 0.1em 0 0"
:size="17"
:color="getThemeColor(item.themeColor)"
>
<IconifyIconOffline :icon="Check" />
</el-icon>
</li>
</ul>
<el-divider>界面显示</el-divider>
<ul class="setting">
<li>
<span class="dark:text-white">灰色模式</span>
<el-switch
v-model="settings.greyVal"
inline-prompt
inactive-color="#a6a6a6"
active-text=""
inactive-text=""
@change="greyChange"
/>
</li>
<li>
<span class="dark:text-white">色弱模式</span>
<el-switch
v-model="settings.weakVal"
inline-prompt
inactive-color="#a6a6a6"
active-text=""
inactive-text=""
@change="weekChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏标签页</span>
<el-switch
v-model="settings.tabsVal"
inline-prompt
inactive-color="#a6a6a6"
active-text=""
inactive-text=""
@change="tagsChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏页脚</span>
<el-switch
v-model="settings.hideFooter"
inline-prompt
inactive-color="#a6a6a6"
active-text=""
inactive-text=""
@change="hideFooterChange"
/>
</li>
<li>
<span class="dark:text-white">侧边栏Logo</span>
<el-switch
v-model="logoVal"
inline-prompt
:active-value="true"
:inactive-value="false"
inactive-color="#a6a6a6"
active-text=""
inactive-text=""
@change="logoChange"
/>
</li>
<li>
<span class="dark:text-white">标签页持久化</span>
<el-switch
v-model="settings.multiTagsCache"
inline-prompt
inactive-color="#a6a6a6"
active-text=""
inactive-text=""
@change="multiTagsCacheChange"
/>
</li>
<li>
<span class="dark:text-white">标签风格</span>
<el-radio-group v-model="markValue" size="small" @change="onChange">
<el-radio label="card">卡片</el-radio>
<el-radio label="smart">灵动</el-radio>
</el-radio-group>
</li>
</ul>
<el-divider />
<el-button
type="danger"
style="width: 90%; margin: 24px 15px"
@click="onReset"
>
<IconifyIconOffline
:icon="Logout"
width="15"
height="15"
style="margin-right: 4px"
<p class="mt-5 mb-3 font-medium text-base dark:text-white">页签风格</p>
<Segmented
:modelValue="markValue === 'smart' ? 0 : 1"
:options="markOptions"
@change="onChange"
/>
清空缓存并返回登录页
</el-button>
<p class="mt-5 mb-1 font-medium text-sm dark:text-white">界面显示</p>
<ul class="setting">
<li>
<span class="dark:text-white">灰色模式</span>
<el-switch
v-model="settings.greyVal"
inline-prompt
active-text="开"
inactive-text="关"
@change="greyChange"
/>
</li>
<li>
<span class="dark:text-white">色弱模式</span>
<el-switch
v-model="settings.weakVal"
inline-prompt
active-text="开"
inactive-text="关"
@change="weekChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏标签页</span>
<el-switch
v-model="settings.tabsVal"
inline-prompt
active-text="开"
inactive-text="关"
@change="tagsChange"
/>
</li>
<li>
<span class="dark:text-white">隐藏页脚</span>
<el-switch
v-model="settings.hideFooter"
inline-prompt
active-text="开"
inactive-text="关"
@change="hideFooterChange"
/>
</li>
<li>
<span class="dark:text-white">Logo</span>
<el-switch
v-model="logoVal"
inline-prompt
:active-value="true"
:inactive-value="false"
active-text="开"
inactive-text="关"
@change="logoChange"
/>
</li>
<li>
<span class="dark:text-white">页签持久化</span>
<el-switch
v-model="settings.multiTagsCache"
inline-prompt
active-text="开"
inactive-text="关"
@change="multiTagsCacheChange"
/>
</li>
</ul>
</div>
</panel>
</template>
@@ -422,41 +391,41 @@ onBeforeMount(() => {
font-weight: 700;
}
.is-select {
border: 2px solid var(--el-color-primary);
:deep(.el-switch__core) {
--el-switch-off-color: var(--pure-switch-off-color);
min-width: 36px;
height: 18px;
}
.setting {
width: 100%;
:deep(.el-switch__core .el-switch__action) {
height: 14px;
}
.theme-color {
height: 20px;
li {
display: flex;
align-items: center;
justify-content: space-between;
margin: 25px;
}
}
float: left;
height: 20px;
margin-right: 8px;
cursor: pointer;
border-radius: 4px;
.pure-datatheme {
display: block;
width: 100%;
height: 50px;
padding-top: 25px;
text-align: center;
&:nth-child(1) {
border: 1px solid #ddd;
}
}
}
.pure-theme {
display: flex;
flex-wrap: wrap;
justify-content: space-around;
width: 100%;
height: 50px;
margin-top: 25px;
gap: 12px;
li {
position: relative;
width: 18%;
height: 45px;
width: 46px;
height: 36px;
overflow: hidden;
cursor: pointer;
background: #f0f2f5;
@@ -517,27 +486,17 @@ onBeforeMount(() => {
}
}
.theme-color {
display: flex;
justify-content: center;
width: 100%;
height: 40px;
margin-top: 20px;
.is-select {
border: 2px solid var(--el-color-primary);
}
.setting {
li {
float: left;
width: 20px;
height: 20px;
margin-top: 8px;
margin-right: 8px;
font-weight: 700;
text-align: center;
cursor: pointer;
border-radius: 2px;
&:nth-child(2) {
border: 1px solid #ddd;
}
display: flex;
align-items: center;
justify-content: space-between;
padding: 4px 0;
font-size: 14px;
}
}
</style>

View File

@@ -108,9 +108,9 @@ watch(
<el-breadcrumb class="!leading-[50px] select-none" separator="/">
<transition-group name="breadcrumb">
<el-breadcrumb-item
class="!inline !items-stretch"
v-for="item in levelList"
:key="item.path"
class="!inline !items-stretch"
>
<a @click.prevent="handleLink(item)">
{{ transformI18n(item.meta.title) }}

View File

@@ -48,8 +48,8 @@ nextTick(() => {
<span>{{ title }}</span>
</div>
<el-menu
router
ref="menuRef"
router
mode="horizontal"
class="horizontal-header-menu"
:default-active="defaultActive"
@@ -78,7 +78,7 @@ nextTick(() => {
:class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh"
>
<span class="check-zh" v-show="locale === 'zh'">
<span v-show="locale === 'zh'" class="check-zh">
<IconifyIconOffline :icon="Check" />
</span>
简体中文
@@ -88,7 +88,7 @@ nextTick(() => {
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn"
>
<span class="check-en" v-show="locale === 'en'">
<span v-show="locale === 'en'" class="check-en">
<IconifyIconOffline :icon="Check" />
</span>
English

View File

@@ -66,6 +66,6 @@ const toggleClick = () => {
width: 100%;
height: 40px;
line-height: 40px;
box-shadow: 0 0 6px -2px var(--el-color-primary);
box-shadow: 0 0 6px -3px var(--el-color-primary);
}
</style>

View File

@@ -61,12 +61,12 @@ watch(
<template>
<div
v-if="device !== 'mobile'"
class="horizontal-header"
v-loading="usePermissionStoreHook().wholeMenus.length === 0"
class="horizontal-header"
>
<el-menu
router
ref="menuRef"
router
mode="horizontal"
class="horizontal-header-menu"
:default-active="defaultActive"
@@ -111,7 +111,7 @@ watch(
:class="['dark:!text-white', getDropdownItemClass(locale, 'zh')]"
@click="translationCh"
>
<span class="check-zh" v-show="locale === 'zh'">
<span v-show="locale === 'zh'" class="check-zh">
<IconifyIconOffline :icon="Check" />
</span>
简体中文
@@ -121,7 +121,7 @@ watch(
:class="['dark:!text-white', getDropdownItemClass(locale, 'en')]"
@click="translationEn"
>
<span class="check-en" v-show="locale === 'en'">
<span v-show="locale === 'en'" class="check-en">
<IconifyIconOffline :icon="Check" />
</span>
English

View File

@@ -60,8 +60,8 @@ const getsubMenuIconStyle = computed((): CSSProperties => {
layout.value === "horizontal"
? "0 5px 0 0"
: isCollapse.value
? "0 auto"
: "0 5px 0 0"
? "0 auto"
: "0 5px 0 0"
};
});
@@ -96,8 +96,8 @@ const getSubMenuDivStyle = computed((): any => {
item?.parentId === null
? "center"
: layout.value === "mix" && item?.pathList?.length === 2
? "center"
: ""
? "center"
: ""
};
};
});
@@ -275,7 +275,6 @@ function resolvePath(routePath) {
{{ transformI18n(props.item.meta.title) }}
</span>
<div
:style="getSubMenuDivStyle(props.item)"
v-if="
!(
isCollapse &&
@@ -283,6 +282,7 @@ function resolvePath(routePath) {
props.item.parentId === null
)
"
:style="getSubMenuDivStyle(props.item)"
>
<el-tooltip
v-if="layout !== 'horizontal'"

View File

@@ -518,15 +518,15 @@ onBeforeUnmount(() => {
</script>
<template>
<div ref="containerDom" class="tags-view" v-if="!showTags">
<div v-if="!showTags" ref="containerDom" class="tags-view">
<span v-show="isShowArrow" class="arrow-left">
<IconifyIconOffline :icon="ArrowLeftSLine" @click="handleScroll(200)" />
</span>
<div ref="scrollbarDom" class="scroll-container">
<div class="tab select-none" ref="tabDom" :style="getTabStyle">
<div ref="tabDom" class="tab select-none" :style="getTabStyle">
<div
:ref="'dynamic' + index"
v-for="(item, index) in multiTags"
:ref="'dynamic' + index"
:key="index"
:class="['scroll-item is-closable', linkIsActive(item)]"
@contextmenu.prevent="openMenu(item, $event)"
@@ -550,8 +550,8 @@ onBeforeUnmount(() => {
<IconifyIconOffline :icon="CloseBold" />
</span>
<div
:ref="'schedule' + index"
v-if="showModel !== 'card'"
:ref="'schedule' + index"
:class="[scheduleIsActive(item)]"
/>
</div>

View File

@@ -46,11 +46,11 @@ onMounted(() => {
<template>
<div
class="frame"
v-loading="loading"
class="frame"
:element-loading-text="t('status.hsLoad')"
>
<iframe :src="frameSrc" class="frame-iframe" ref="frameRef" />
<iframe ref="frameRef" :src="frameSrc" class="frame-iframe" />
</div>
</template>

View File

@@ -1,9 +1,14 @@
import { ref } from "vue";
import { getConfig } from "@/config";
import { useLayout } from "./useLayout";
import { themeColorsType } from "../types";
import { useGlobal } from "@pureadmin/utils";
import { removeToken } from "@/utils/auth";
import { routerArrays } from "@/layout/types";
import { router, resetRouter } from "@/router";
import type { themeColorsType } from "../types";
import { useAppStoreHook } from "@/store/modules/app";
import { useGlobal, storageLocal } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import {
darken,
lighten,
@@ -13,42 +18,53 @@ import {
export function useDataThemeChange() {
const { layoutTheme, layout } = useLayout();
const themeColors = ref<Array<themeColorsType>>([
/* 道奇蓝(默认) */
{ color: "#1b2a47", themeColor: "default" },
/* 亮白色 */
{ color: "#ffffff", themeColor: "light" },
/* 道奇蓝 */
{ color: "#1b2a47", themeColor: "default" },
/* 深紫罗兰色 */
{ color: "#722ed1", themeColor: "saucePurple" },
/* 深粉色 */
{ color: "#eb2f96", themeColor: "pink" },
/* 猩红色 */
{ color: "#f5222d", themeColor: "dusk" },
/* 橙红色 */
{ color: "#fa541c", themeColor: "volcano" },
/* 金色 */
{ color: "#fadb14", themeColor: "yellow" },
/* 绿宝石 */
{ color: "#13c2c2", themeColor: "mingQing" },
/* 酸橙绿 */
{ color: "#52c41a", themeColor: "auroraGreen" },
/* 深粉色 */
{ color: "#eb2f96", themeColor: "pink" },
/* 深紫罗兰色 */
{ color: "#722ed1", themeColor: "saucePurple" }
{ color: "#52c41a", themeColor: "auroraGreen" }
]);
const { $storage } = useGlobal<GlobalPropertiesApi>();
const dataTheme = ref<boolean>($storage?.layout?.darkMode);
const body = document.documentElement as HTMLElement;
function toggleClass(flag: boolean, clsName: string, target?: HTMLElement) {
const targetEl = target || document.body;
let { className } = targetEl;
className = className.replace(clsName, "").trim();
targetEl.className = flag ? `${className} ${clsName} ` : className;
}
/** 设置导航主题色 */
function setLayoutThemeColor(theme = getConfig().Theme ?? "default") {
function setLayoutThemeColor(
theme = getConfig().Theme ?? "light",
isClick = true
) {
layoutTheme.value.theme = theme;
toggleTheme({
scopeName: `layout-theme-${theme}`
});
// 如果非isClick保留之前的themeColor
const storageThemeColor = $storage.layout.themeColor;
$storage.layout = {
layout: layout.value,
theme,
darkMode: dataTheme.value,
sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: $storage.layout?.epThemeColor
epThemeColor: $storage.layout?.epThemeColor,
themeColor: isClick ? theme : storageThemeColor
};
if (theme === "default" || theme === "light") {
@@ -78,27 +94,46 @@ export function useDataThemeChange() {
}
};
/** 日间、夜间主题切换 */
/** 亮色、暗色整体风格切换 */
function dataThemeChange() {
/* 如果当前是light夜间主题默认切换到default主题 */
if (useEpThemeStoreHook().epTheme === "light" && dataTheme.value) {
setLayoutThemeColor("default");
setLayoutThemeColor("default", false);
} else {
setLayoutThemeColor(useEpThemeStoreHook().epTheme);
setLayoutThemeColor(useEpThemeStoreHook().epTheme, false);
}
if (dataTheme.value) {
document.documentElement.classList.add("dark");
} else {
if ($storage.layout.themeColor === "light") {
setLayoutThemeColor("light", false);
}
document.documentElement.classList.remove("dark");
}
}
/** 清空缓存并返回登录页 */
function onReset() {
removeToken();
storageLocal().clear();
const { Grey, Weak, MultiTagsCache, EpThemeColor, Layout } = getConfig();
useAppStoreHook().setLayout(Layout);
setEpThemeColor(EpThemeColor);
useMultiTagsStoreHook().multiTagsCacheChange(MultiTagsCache);
toggleClass(Grey, "html-grey", document.querySelector("html"));
toggleClass(Weak, "html-weakness", document.querySelector("html"));
router.push("/login");
useMultiTagsStoreHook().handleTags("equal", [...routerArrays]);
resetRouter();
}
return {
body,
dataTheme,
layoutTheme,
themeColors,
onReset,
toggleClass,
dataThemeChange,
setEpThemeColor,
setLayoutThemeColor

View File

@@ -24,10 +24,11 @@ export function useLayout() {
if (!$storage.layout) {
$storage.layout = {
layout: $config?.Layout ?? "vertical",
theme: $config?.Theme ?? "default",
theme: $config?.Theme ?? "light",
darkMode: $config?.DarkMode ?? false,
sidebarStatus: $config?.SidebarStatus ?? true,
epThemeColor: $config?.EpThemeColor ?? "#409EFF"
epThemeColor: $config?.EpThemeColor ?? "#409EFF",
themeColor: $config?.Theme ?? "light"
};
}
/** 灰色模式、色弱模式、隐藏标签页 */

View File

@@ -2,10 +2,10 @@ import { storeToRefs } from "pinia";
import { getConfig } from "@/config";
import { useRouter } from "vue-router";
import { emitter } from "@/utils/mitt";
import { routeMetaType } from "../types";
import userAvatar from "@/assets/user.jpg";
import { getTopMenu } from "@/router/utils";
import { useGlobal } from "@pureadmin/utils";
import type { routeMetaType } from "../types";
import { transformI18n } from "@/plugins/i18n";
import { router, remainingPaths } from "@/router";
import { computed, type CSSProperties } from "vue";

View File

@@ -4,10 +4,10 @@ import {
computed,
reactive,
onMounted,
CSSProperties,
type CSSProperties,
getCurrentInstance
} from "vue";
import { tagsViewsType } from "../types";
import type { tagsViewsType } from "../types";
import { useRoute, useRouter } from "vue-router";
import { transformI18n, $t } from "@/plugins/i18n";
import { responsiveStorageNameSpace } from "@/config";

View File

@@ -68,7 +68,8 @@ function setTheme(layoutModel: string) {
theme: $storage.layout?.theme,
darkMode: $storage.layout?.darkMode,
sidebarStatus: $storage.layout?.sidebarStatus,
epThemeColor: $storage.layout?.epThemeColor
epThemeColor: $storage.layout?.epThemeColor,
themeColor: $storage.layout?.themeColor
};
}

View File

@@ -2,10 +2,23 @@
* @description ⚠️:此文件仅供主题插件使用,请不要在此文件中导出别的工具函数(仅在页面加载前运行)
*/
import { type multipleScopeVarsOptions } from "@pureadmin/theme";
import type { multipleScopeVarsOptions } from "@pureadmin/theme";
/** 预设主题色 */
const themeColors = {
/* 亮白色 */
light: {
subMenuActiveText: "#000000d9",
menuBg: "#fff",
menuHover: "#e0ebf6",
subMenuBg: "#fff",
subMenuActiveBg: "#e0ebf6",
menuText: "rgb(0 0 0 / 60%)",
sidebarLogo: "#fff",
menuTitleHover: "#000",
menuActiveBefore: "#4091f7"
},
/* 道奇蓝 */
default: {
subMenuActiveText: "#fff",
menuBg: "#001529",
@@ -17,72 +30,19 @@ const themeColors = {
menuTitleHover: "#fff",
menuActiveBefore: "#4091f7"
},
light: {
subMenuActiveText: "#409eff",
menuBg: "#fff",
menuHover: "#e0ebf6",
subMenuBg: "#fff",
subMenuActiveBg: "#e0ebf6",
menuText: "#7a80b4",
sidebarLogo: "#fff",
menuTitleHover: "#000",
menuActiveBefore: "#4091f7"
},
dusk: {
/* 深紫罗兰色 */
saucePurple: {
subMenuActiveText: "#fff",
menuBg: "#2a0608",
menuHover: "#e13c39",
menuBg: "#130824",
menuHover: "#693ac9",
subMenuBg: "#000",
subMenuActiveBg: "#e13c39",
menuText: "rgb(254 254 254 / 65.1%)",
sidebarLogo: "#42090c",
menuTitleHover: "#fff",
menuActiveBefore: "#e13c39"
},
volcano: {
subMenuActiveText: "#fff",
menuBg: "#2b0e05",
menuHover: "#e85f33",
subMenuBg: "#0f0603",
subMenuActiveBg: "#e85f33",
menuText: "rgb(254 254 254 / 65%)",
sidebarLogo: "#441708",
menuTitleHover: "#fff",
menuActiveBefore: "#e85f33"
},
yellow: {
subMenuActiveText: "#d25f00",
menuBg: "#2b2503",
menuHover: "#f6da4d",
subMenuBg: "#0f0603",
subMenuActiveBg: "#f6da4d",
menuText: "rgb(254 254 254 / 65%)",
sidebarLogo: "#443b05",
menuTitleHover: "#fff",
menuActiveBefore: "#f6da4d"
},
mingQing: {
subMenuActiveText: "#fff",
menuBg: "#032121",
menuHover: "#59bfc1",
subMenuBg: "#000",
subMenuActiveBg: "#59bfc1",
subMenuActiveBg: "#693ac9",
menuText: "#7a80b4",
sidebarLogo: "#053434",
sidebarLogo: "#1f0c38",
menuTitleHover: "#fff",
menuActiveBefore: "#59bfc1"
},
auroraGreen: {
subMenuActiveText: "#fff",
menuBg: "#0b1e15",
menuHover: "#60ac80",
subMenuBg: "#000",
subMenuActiveBg: "#60ac80",
menuText: "#7a80b4",
sidebarLogo: "#112f21",
menuTitleHover: "#fff",
menuActiveBefore: "#60ac80"
menuActiveBefore: "#693ac9"
},
/* 深粉色 */
pink: {
subMenuActiveText: "#fff",
menuBg: "#28081a",
@@ -94,16 +54,53 @@ const themeColors = {
menuTitleHover: "#fff",
menuActiveBefore: "#d84493"
},
saucePurple: {
/* 猩红色 */
dusk: {
subMenuActiveText: "#fff",
menuBg: "#130824",
menuHover: "#693ac9",
menuBg: "#2a0608",
menuHover: "#e13c39",
subMenuBg: "#000",
subMenuActiveBg: "#693ac9",
menuText: "#7a80b4",
sidebarLogo: "#1f0c38",
subMenuActiveBg: "#e13c39",
menuText: "rgb(254 254 254 / 65.1%)",
sidebarLogo: "#42090c",
menuTitleHover: "#fff",
menuActiveBefore: "#693ac9"
menuActiveBefore: "#e13c39"
},
/* 橙红色 */
volcano: {
subMenuActiveText: "#fff",
menuBg: "#2b0e05",
menuHover: "#e85f33",
subMenuBg: "#0f0603",
subMenuActiveBg: "#e85f33",
menuText: "rgb(254 254 254 / 65%)",
sidebarLogo: "#441708",
menuTitleHover: "#fff",
menuActiveBefore: "#e85f33"
},
/* 绿宝石 */
mingQing: {
subMenuActiveText: "#fff",
menuBg: "#032121",
menuHover: "#59bfc1",
subMenuBg: "#000",
subMenuActiveBg: "#59bfc1",
menuText: "#7a80b4",
sidebarLogo: "#053434",
menuTitleHover: "#fff",
menuActiveBefore: "#59bfc1"
},
/* 酸橙绿 */
auroraGreen: {
subMenuActiveText: "#fff",
menuBg: "#0b1e15",
menuHover: "#60ac80",
subMenuBg: "#000",
subMenuActiveBg: "#60ac80",
menuText: "#7a80b4",
sidebarLogo: "#112f21",
menuTitleHover: "#fff",
menuActiveBefore: "#60ac80"
}
};

View File

@@ -4,9 +4,9 @@ import { setupStore } from "@/store";
import ElementPlus from "element-plus";
import { useI18n } from "@/plugins/i18n";
import { getPlatformConfig } from "./config";
import { createApp, Directive } from "vue";
import { MotionPlugin } from "@vueuse/motion";
import { useEcharts } from "@/plugins/echarts";
import { createApp, type Directive } from "vue";
import { injectResponsiveStorage } from "@/utils/responsive";
import Table from "@pureadmin/table";
@@ -45,6 +45,14 @@ app.component("FontIcon", FontIcon);
import { Auth } from "@/components/ReAuth";
app.component("Auth", Auth);
// 全局注册`vue-tippy`
import "tippy.js/dist/tippy.css";
import "tippy.js/animations/perspective.css";
import VueTippy from "vue-tippy";
app.use(VueTippy, {
defaultProps: { animation: "perspective" }
});
getPlatformConfig(app).then(async config => {
setupStore(app);
app.use(router);

View File

@@ -1,14 +0,0 @@
import { createProdMockServer } from "vite-plugin-mock/es/createProdMockServer";
const modules: Record<string, any> = import.meta.glob("../mock/*.ts", {
eager: true
});
const mockModules = [];
Object.keys(modules).forEach(key => {
mockModules.push(...modules[key].default);
});
export function setupProdMockServer() {
createProdMockServer(mockModules);
}

View File

@@ -1,4 +1,4 @@
import { App, Component } from "vue";
import type { App, Component } from "vue";
import {
ElTag,
ElAffix,

View File

@@ -1,6 +1,6 @@
// 多组件库的国际化和本地项目国际化兼容
import { App, WritableComputedRef } from "vue";
import { type I18n, createI18n } from "vue-i18n";
import type { App, WritableComputedRef } from "vue";
import { responsiveStorageNameSpace } from "@/config";
import { storageLocal, isObject } from "@pureadmin/utils";
@@ -8,16 +8,20 @@ import { storageLocal, isObject } from "@pureadmin/utils";
import enLocale from "element-plus/dist/locale/en.mjs";
import zhLocale from "element-plus/dist/locale/zh-cn.mjs";
function siphonI18n(prefix = "zh-CN") {
return Object.fromEntries(
const siphonI18n = (function () {
// 仅初始化一次国际化配置
let cache = Object.fromEntries(
Object.entries(
import.meta.glob("../../locales/*.y(a)?ml", { eager: true })
).map(([key, value]: any) => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)[1];
return [matched, value.default];
})
)[prefix];
}
);
return (prefix = "zh-CN") => {
return cache[prefix];
};
})();
export const localesConfigs = {
zh: {
@@ -33,7 +37,7 @@ export const localesConfigs = {
/** 获取对象中所有嵌套对象的key键并将它们用点号分割组成字符串 */
function getObjectKeys(obj) {
const stack = [];
const keys = [];
const keys: Set<string> = new Set();
stack.push({ obj, key: "" });
@@ -46,7 +50,7 @@ function getObjectKeys(obj) {
if (obj[k] && isObject(obj[k])) {
stack.push({ obj: obj[k], key: newKey });
} else {
keys.push(newKey);
keys.add(newKey);
}
}
}
@@ -54,6 +58,17 @@ function getObjectKeys(obj) {
return keys;
}
/** 将展开的key缓存 */
const keysCache: Map<string, Set<string>> = new Map();
const flatI18n = (prefix = "zh-CN") => {
let cache = keysCache.get(prefix);
if (!cache) {
cache = getObjectKeys(siphonI18n(prefix));
keysCache.set(prefix, cache);
}
return cache;
};
/**
* 国际化转换工具函数自动读取根目录locales文件夹下文件进行国际化匹配
* @param message message
@@ -73,9 +88,9 @@ export function transformI18n(message: any = "") {
const key = message.match(/(\S*)\./)?.input;
if (key && getObjectKeys(siphonI18n("zh-CN")).find(item => item === key)) {
if (key && flatI18n("zh-CN").has(key)) {
return i18n.global.t.call(i18n.global.locale, message);
} else if (!key && Object.keys(siphonI18n("zh-CN")).includes(message)) {
} else if (!key && Object.hasOwn(siphonI18n("zh-CN"), message)) {
// 兼容非嵌套形式的国际化写法
return i18n.global.t.call(i18n.global.locale, message);
} else {

View File

@@ -20,10 +20,10 @@ import {
formatFlatteningRoutes
} from "./utils";
import {
Router,
type Router,
createRouter,
RouteRecordRaw,
RouteComponent
type RouteRecordRaw,
type RouteComponent
} from "vue-router";
import {
type DataInfo,

View File

@@ -183,4 +183,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -18,4 +18,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -171,4 +171,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -20,4 +20,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -36,4 +36,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -19,4 +19,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -22,4 +22,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -19,4 +19,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -24,4 +24,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -21,4 +21,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -19,4 +19,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -79,4 +79,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -21,4 +21,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -21,4 +21,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -21,4 +21,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -39,4 +39,4 @@ export default [
rank: 103
}
}
] as Array<RouteConfigsTable>;
] satisfies Array<RouteConfigsTable>;

View File

@@ -27,4 +27,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -28,4 +28,4 @@ export default {
}
}
]
} as RouteConfigsTable;
} satisfies RouteConfigsTable;

View File

@@ -1,7 +1,7 @@
import {
RouterHistory,
RouteRecordRaw,
RouteComponent,
type RouterHistory,
type RouteRecordRaw,
type RouteComponent,
createWebHistory,
createWebHashHistory
} from "vue-router";
@@ -17,7 +17,7 @@ import {
isIncludeAllChildren
} from "@pureadmin/utils";
import { getConfig } from "@/config";
import { menuType } from "@/layout/types";
import type { menuType } from "@/layout/types";
import { buildHierarchyTree } from "@/utils/tree";
import { userKey, type DataInfo } from "@/utils/auth";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";

View File

@@ -1,6 +1,6 @@
import { store } from "@/store";
import { appType } from "./types";
import { defineStore } from "pinia";
import type { appType } from "./types";
import { getConfig, responsiveStorageNameSpace } from "@/config";
import { deviceDetection, storageLocal } from "@pureadmin/utils";

View File

@@ -23,8 +23,6 @@ export const useEpThemeStore = defineStore({
fill(state) {
if (state.epTheme === "light") {
return "#409eff";
} else if (state.epTheme === "yellow") {
return "#d25f00";
} else {
return "#fff";
}

View File

@@ -1,8 +1,8 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { routerArrays } from "@/layout/types";
import { multiType, positionType } from "./types";
import { responsiveStorageNameSpace } from "@/config";
import type { multiType, positionType } from "./types";
import { isEqual, isBoolean, isUrl, storageLocal } from "@pureadmin/utils";
export const useMultiTagsStore = defineStore({

View File

@@ -1,6 +1,6 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { cacheType } from "./types";
import type { cacheType } from "./types";
import { constantMenus } from "@/router";
import { useMultiTagsStoreHook } from "./multiTags";
import { debounce, getKeyList } from "@pureadmin/utils";

View File

@@ -1,7 +1,7 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { setType } from "./types";
import { getConfig } from "@/config";
import type { setType } from "./types";
export const useSettingStore = defineStore({
id: "pure-setting",

View File

@@ -1,4 +1,4 @@
import { RouteRecordName } from "vue-router";
import type { RouteRecordName } from "vue-router";
export type cacheType = {
mode: string;

View File

@@ -1,11 +1,11 @@
import { defineStore } from "pinia";
import { store } from "@/store";
import { userType } from "./types";
import type { userType } from "./types";
import { routerArrays } from "@/layout/types";
import { router, resetRouter } from "@/router";
import { storageLocal } from "@pureadmin/utils";
import { getLogin, refreshTokenApi } from "@/api/user";
import { UserResult, RefreshTokenResult } from "@/api/user";
import type { UserResult, RefreshTokenResult } from "@/api/user";
import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
import { type DataInfo, setToken, removeToken, userKey } from "@/utils/auth";

View File

@@ -2,11 +2,18 @@
/* 暗黑模式适配 */
html.dark {
/* 自定义深色背景颜色 */
// --el-bg-color: #020409;
$border-style: #303030;
$color-white: #fff;
/* 自定义深色背景颜色 */
// --el-bg-color: #020409;
/* 常用border-color 需要时可取用 */
--pure-border-color: rgb(253 253 253 / 12%);
/* switch关闭状态下的color 需要时可取用 */
--pure-switch-off-color: #ffffff3f;
.navbar,
.tags-view,
.contextmenu,

View File

@@ -50,12 +50,6 @@
padding: 0 !important;
}
/* 自定义 tooltip 的类名 */
.pure-tooltip {
// 右侧操作面板right-panel类名的z-index为40000tooltip需要大于它才能显示
z-index: 41000 !important;
}
/* nprogress 适配 element-plus 的主题色 */
#nprogress {
& .bar {

View File

@@ -7,6 +7,12 @@
:root {
/* 左侧菜单展开、收起动画时长 */
--pure-transition-duration: 0.3s;
/* 常用border-color 需要时可取用 */
--pure-border-color: rgb(5 5 5 / 6%);
/* switch关闭状态下的color 需要时可取用 */
--pure-switch-off-color: #a6a6a6;
}
/* 灰色模式 */

View File

@@ -83,7 +83,7 @@
overflow: hidden;
font-size: 0;
background: $menuBg;
box-shadow: 0 0 1px #888;
border-right: 1px solid var(--pure-border-color);
/* 展开动画 */
transition: width var(--pure-transition-duration);
@@ -193,7 +193,7 @@
.el-menu .el-menu--inline .el-sub-menu__title,
& .el-sub-menu .el-menu-item {
min-width: $sideBarWidth !important;
font-size: 12px;
font-size: 14px;
background-color: $subMenuBg !important;
}
@@ -248,7 +248,7 @@
.el-menu-item {
span {
font-size: 12px;
font-size: 14px;
}
}
}
@@ -271,7 +271,7 @@
/* 子菜单中还有子菜单 */
.el-menu .el-sub-menu__title {
min-width: $sideBarWidth !important;
font-size: 12px;
font-size: 14px;
background-color: $subMenuBg !important;
}
@@ -334,7 +334,7 @@
background-color: $subMenuBg;
span {
font-size: 12px;
font-size: 14px;
}
}
@@ -352,7 +352,7 @@
/* 子菜单中还有子菜单 */
.el-menu .el-sub-menu__title {
min-width: $sideBarWidth !important;
font-size: 12px;
font-size: 14px;
background-color: $subMenuBg !important;
&:hover {

View File

@@ -1,9 +1,9 @@
import Axios, {
AxiosInstance,
AxiosRequestConfig,
CustomParamsSerializer
type AxiosInstance,
type AxiosRequestConfig,
type CustomParamsSerializer
} from "axios";
import {
import type {
PureHttpError,
RequestMethods,
PureHttpResponse,

View File

@@ -1,4 +1,4 @@
import Axios, {
import type {
Method,
AxiosError,
AxiosResponse,

View File

@@ -1,5 +1,5 @@
import forage from "localforage";
import { LocalForage, ProxyStorage, ExpiresData } from "./types.d";
import type { LocalForage, ProxyStorage, ExpiresData } from "./types.d";
class StorageProxy implements ProxyStorage {
protected storage: LocalForage;

View File

@@ -1,4 +1,4 @@
import { type VNode } from "vue";
import type { VNode } from "vue";
import { isFunction } from "@pureadmin/utils";
import { type MessageHandler, ElMessage } from "element-plus";

View File

@@ -2,8 +2,8 @@ import type { CSSProperties, VNodeChild } from "vue";
import {
createTypes,
toValidableType,
VueTypesInterface,
VueTypeValidableDef
type VueTypesInterface,
type VueTypeValidableDef
} from "vue-types";
export type VueNode = VNodeChild | JSX.Element;

View File

@@ -1,5 +1,5 @@
// 响应式storage
import { App } from "vue";
import type { App } from "vue";
import Storage from "responsive-storage";
import { routerArrays } from "@/layout/types";
import { responsiveStorageNameSpace } from "@/config";
@@ -15,11 +15,13 @@ export const injectResponsiveStorage = (app: App, config: PlatformConfigs) => {
// layout模式以及主题
layout: Storage.getData("layout", nameSpace) ?? {
layout: config.Layout ?? "vertical",
theme: config.Theme ?? "default",
theme: config.Theme ?? "light",
darkMode: config.DarkMode ?? false,
sidebarStatus: config.SidebarStatus ?? true,
epThemeColor: config.EpThemeColor ?? "#409EFF"
epThemeColor: config.EpThemeColor ?? "#409EFF",
themeColor: config.Theme ?? "light" // 主题色对应项目配置中的主题色与theme不同的是它不会受到亮色、暗色整体风格切换的影响只会在手动点击主题色时改变
},
// 项目配置-界面显示
configure: Storage.getData("configure", nameSpace) ?? {
grey: config.Grey ?? false,
weak: config.Weak ?? false,

View File

@@ -40,8 +40,8 @@ import { subBefore, getQueryMap } from "@pureadmin/utils";
setToken(params);
// 删除不需要显示在 url 的参数
delete params["roles"];
delete params["accessToken"];
delete params.roles;
delete params.accessToken;
const newUrl = `${location.origin}${location.pathname}${subBefore(
location.hash,

View File

@@ -32,8 +32,8 @@ const handleChange = value => {
<span class="imp">
1. 二级联动不带全部选项
<el-cascader
:options="provinceAndCityData"
v-model="selectedOptions1"
:options="provinceAndCityData"
@change="handleChange"
/>
</span>
@@ -61,8 +61,8 @@ const handleChange = value => {
<span class="imp">
2. 二级联动带有全部选项
<el-cascader
:options="provinceAndCityDataPlus"
v-model="selectedOptions3"
:options="provinceAndCityDataPlus"
@change="handleChange"
/>
</span>
@@ -90,8 +90,8 @@ const handleChange = value => {
<span class="imp">
3. 三级联动不带全部选项
<el-cascader
:options="regionData"
v-model="selectedOptions2"
:options="regionData"
@change="handleChange"
/>
</span>
@@ -121,8 +121,8 @@ const handleChange = value => {
<span class="imp">
4. 三级联动"全部选项"
<el-cascader
:options="regionDataPlus"
v-model="selectedOptions4"
:options="regionDataPlus"
@change="handleChange"
/>
</span>

View File

@@ -62,13 +62,13 @@ function onReset() {
<div class="mb-2">
防抖指令连续输入只会执行第一次点击事件立即执行
<el-input
v-model="search"
v-optimize="{
event: 'input',
fn: onInput,
immediate: true,
timeout: 1000
}"
v-model="search"
class="!w-[200px]"
clearable
@clear="onInput"
@@ -77,8 +77,8 @@ function onReset() {
<div class="mb-2">
防抖指令(连续输入,只会执行最后一次事件,延后执行)
<el-input
v-optimize="{ event: 'input', fn: onInputTwo, timeout: 400 }"
v-model="searchTwo"
v-optimize="{ event: 'input', fn: onInputTwo, timeout: 400 }"
class="!w-[200px]"
clearable
/>
@@ -86,13 +86,13 @@ function onReset() {
<div>
防抖指令(连续输入,只会执行最后一次事件,延后执行,传参用法)
<el-input
v-model="searchThree"
v-optimize="{
event: 'input',
fn: onInputThree,
timeout: 400,
params: { name: '小明', sex: '男' }
}"
v-model="searchThree"
class="!w-[200px]"
clearable
/>
@@ -103,8 +103,8 @@ function onReset() {
<div class="mb-2">
节流指令(连续输入,每一秒只会执行一次事件)
<el-input
v-optimize:throttle="{ event: 'input', fn: onInputFour, timeout: 1000 }"
v-model="searchFour"
v-optimize:throttle="{ event: 'input', fn: onInputFour, timeout: 1000 }"
class="!w-[200px]"
clearable
/>
@@ -112,12 +112,12 @@ function onReset() {
<div>
节流指令(连续输入,每一秒只会执行一次事件,传参用法)
<el-input
v-model="searchFive"
v-optimize:throttle="{
event: 'input',
fn: onInputFive,
params: { name: '小明', sex: '男' }
}"
v-model="searchFive"
class="!w-[200px]"
clearable
/>
@@ -127,7 +127,7 @@ function onReset() {
<div class="mb-2">
文本复制指令(双击输入框内容即可复制)
<el-input v-copy="searchSix" v-model="searchSix" class="!w-[200px]" />
<el-input v-model="searchSix" v-copy="searchSix" class="!w-[200px]" />
</div>
<div>
文本复制指令(自定义触发事件,单击复制)

View File

@@ -61,8 +61,8 @@ const filterMethod = (query: string, node: treeNode) => {
</div>
</template>
<el-input
class="mb-4"
v-model="query"
class="mb-4"
placeholder="请输入关键字查找"
clearable
@input="onQueryChanged"

View File

@@ -49,8 +49,8 @@ const onPrint = () => {
</div>
</template>
<div
class="h-[calc(100vh-239px)]"
v-loading="loading"
class="h-[calc(100vh-239px)]"
:element-loading-text="t('status.hsLoad')"
>
<div class="flex justify-between items-center h-9">
@@ -59,9 +59,9 @@ const onPrint = () => {
</div>
<div v-else>
<el-pagination
v-model:current-page="currentPage"
background
layout="prev, slot, next"
v-model:current-page="currentPage"
:page-size="1"
:total="pageCount"
>
@@ -98,8 +98,8 @@ const onPrint = () => {
</div>
<el-scrollbar>
<vue-pdf-embed
class="h-full container overflow-auto"
ref="pdfRef"
class="h-full container overflow-auto"
:rotation="rotations[currentRotation]"
:page="currentPage"
:source="source"

View File

@@ -103,12 +103,12 @@ const tableData: User[] = [
</template>
<el-row :gutter="24">
<el-col
v-motion
:xs="24"
:sm="24"
:md="24"
:lg="24"
:xl="24"
v-motion
:initial="{
opacity: 0,
y: 100
@@ -138,12 +138,12 @@ const tableData: User[] = [
<el-divider />
<el-col
v-motion
:xs="11"
:sm="11"
:md="11"
:lg="11"
:xl="11"
v-motion
:initial="{
opacity: 0,
y: 100
@@ -161,12 +161,12 @@ const tableData: User[] = [
</el-col>
<el-col
v-motion
:xs="11"
:sm="11"
:md="11"
:lg="11"
:xl="11"
v-motion
:initial="{
opacity: 0,
y: 100

View File

@@ -24,7 +24,7 @@ const wasmPath = getPath("capture.worker.wasm");
loadScript({
src
}).then(mgs => {
if (mgs === "success") {
if (mgs[0].message === "加载成功") {
// @ts-expect-error
captureUtil.value = cheetahCapture.initCapture({
workerPath,
@@ -155,9 +155,9 @@ onBeforeUnmount(() => {
/>
</div>
<div
id="canvas-container"
v-loading="loading"
element-loading-text="温馨提示可左右拖拽图片并单击选取所需的帧图片"
id="canvas-container"
class="w-full h-[200px] overflow-hidden mt-6"
/>
</el-card>

View File

@@ -23,9 +23,9 @@ const filteredItems = computed(() => {
<div class="flex-ac mb-4 shadow-2xl">
水平模式 horizontal
<el-input
v-model="search"
class="mr-2 !w-[1/1.5]"
clearable
v-model="search"
placeholder="Filter..."
style="width: 300px"
/>

View File

@@ -23,9 +23,9 @@ const filteredItems = computed(() => {
<div class="flex-ac mb-4 shadow-2xl">
垂直模式 vertical
<el-input
v-model="search"
class="!w-[350px]"
clearable
v-model="search"
placeholder="Filter..."
/>
</div>

View File

@@ -42,15 +42,15 @@ onBeforeUnmount(() => {
</template>
<span> 请输入要创建水印的值</span>
<el-input
v-model="value"
class="mb-4 mr-4"
style="width: 200px"
v-model="value"
clearable
/>
<span>请选择要创建水印的颜色</span>
<el-color-picker v-model="color" show-alpha />
<br />
<el-button @click="setWatermark(value, { fillStyle: color })">
<el-button @click="setWatermark(value, { color })">
创建整页水印
</el-button>
<el-button
@@ -117,7 +117,7 @@ onBeforeUnmount(() => {
<el-button
@click="
setLocalWatermark('局部水印', {
fillStyle: color,
color,
width: 140,
height: 60
})

View File

@@ -4,15 +4,10 @@ import { getTime } from "@pureadmin/utils";
import { Play, Pause, Forward, Rewind } from "./svg";
import { ref, onMounted, onBeforeUnmount } from "vue";
import "tippy.js/dist/tippy.css";
import "tippy.js/animations/scale.css";
import { directive as tippy } from "vue-tippy";
defineOptions({
name: "Wavesurfer"
});
const vTippy = tippy;
const loading = ref(true);
const wavesurfer = ref(null);
const wavesurferRef = ref();
@@ -105,41 +100,38 @@ onBeforeUnmount(() => {
element-loading-background="transparent"
>
<div ref="wavesurferRef" />
<div class="flex justify-between" v-show="totalTime">
<div v-show="totalTime" class="flex justify-between">
<span class="text-[#81888f]">00:00</span>
<h1 class="text-4xl mt-2">{{ curTime }}</h1>
<span class="text-[#81888f]">{{ totalTime }}</span>
</div>
<div class="flex mt-2 w-[180px] justify-around m-auto" v-show="totalTime">
<div v-show="totalTime" class="flex mt-2 w-[180px] justify-around m-auto">
<Rewind
class="cursor-pointer"
v-tippy="{
content: '快退(可长按)',
placement: 'bottom',
animation: 'scale'
placement: 'bottom'
}"
v-longpress:0:100="() => wavesurfer?.skip(-1)"
class="cursor-pointer"
/>
<div
class="cursor-pointer"
v-tippy="{
content: isPlay ? '暂停' : '播放',
placement: 'bottom',
animation: 'scale'
placement: 'bottom'
}"
class="cursor-pointer"
@click="wavesurfer?.playPause()"
>
<Play v-if="isPlay" v-motion-pop />
<Pause v-else v-motion-pop />
</div>
<Forward
class="cursor-pointer"
v-tippy="{
content: '快进(可长按)',
placement: 'bottom',
animation: 'scale'
placement: 'bottom'
}"
v-longpress:0:100="() => wavesurfer?.skip(1)"
class="cursor-pointer"
/>
</div>
</div>

View File

@@ -57,11 +57,11 @@ Object.keys(devDependencies).forEach(key => {
</template>
<el-descriptions border>
<el-descriptions-item
v-for="(item, index) in schema"
:key="index"
:label="item.label"
label-align="left"
align="left"
v-for="(item, index) in schema"
:key="index"
>
<a
:href="'https://www.npmjs.com/package/' + item.label"
@@ -81,11 +81,11 @@ Object.keys(devDependencies).forEach(key => {
</template>
<el-descriptions border>
<el-descriptions-item
v-for="(item, index) in devSchema"
:key="index"
:label="item.label"
label-align="left"
align="left"
v-for="(item, index) in devSchema"
:key="index"
>
<a
:href="'https://www.npmjs.com/package/' + item.label"

View File

@@ -120,8 +120,8 @@ function addDanmu() {
<div class="flex gap-5">
<vue-danmaku
ref="danmaku"
class="demo"
v-model:danmus="danmus"
class="demo"
isSuspend
v-bind="config"
>
@@ -176,9 +176,9 @@ function addDanmu() {
</p>
<p class="flex">
<el-input
v-model="danmuMsg"
type="text"
placeholder="输入评论后,回车发送弹幕"
v-model="danmuMsg"
@keyup.enter="addDanmu"
/>
</p>

View File

@@ -27,15 +27,15 @@ const newFormInline = ref(props.formInline);
<el-form :model="newFormInline">
<el-form-item label="姓名">
<el-input
class="!w-[220px]"
v-model="newFormInline.user"
class="!w-[220px]"
placeholder="请输入姓名"
/>
</el-form-item>
<el-form-item label="城市">
<el-select
class="!w-[220px]"
v-model="newFormInline.region"
class="!w-[220px]"
placeholder="请选择城市"
>
<el-option label="上海" value="上海" />

View File

@@ -18,5 +18,5 @@ const data = useVModel(props, "data", emit);
</script>
<template>
<el-input class="!w-[220px]" v-model="data" placeholder="请输入内容" />
<el-input v-model="data" class="!w-[220px]" placeholder="请输入内容" />
</template>

View File

@@ -110,10 +110,10 @@ onMounted(() => {
<draggable
v-model="lists"
item-key="name"
@change="change"
chosen-class="chosen"
force-fallback="true"
animation="300"
@change="change"
>
<template #item="{ element, index }">
<div class="item-single">{{ element.name }} {{ index }}</div>
@@ -132,9 +132,9 @@ onMounted(() => {
<!-- 拖拽实现元素位置切换 -->
<div class="cut-container">
<div
class="item-cut"
v-for="(item, index) in cutLists"
:key="index"
class="item-cut"
>
<p>{{ item.name }}</p>
</div>

View File

@@ -163,7 +163,7 @@ function onChange({ index, option }) {
{{ optionsBasis[value].label }}
</span>
</p>
<Segmented :options="optionsBasis" v-model="value" />
<Segmented v-model="value" :options="optionsBasis" />
<el-divider />
<p class="mb-2">禁用</p>
<Segmented :options="optionsDisabled" />

View File

@@ -28,9 +28,9 @@ const selectedVal = ({ left, right }): void => {
<template>
<div>
<el-card
class="box-card"
v-for="(item, key) in dataLists"
:key="key"
class="box-card"
shadow="never"
>
<template #header>
@@ -41,10 +41,10 @@ const selectedVal = ({ left, right }): void => {
<Selector
:HsKey="key"
:echo="item.echo"
@selectedVal="selectedVal"
:disabled="item.disabled"
@selectedVal="selectedVal"
/>
<h4 class="mt-3" v-if="!item.disabled">选中范围{{ selectRange }}</h4>
<h4 v-if="!item.disabled" class="mt-3">选中范围{{ selectRange }}</h4>
</el-card>
</div>
</template>

Some files were not shown because too many files have changed in this diff Show More