chore: 🔨 合并冲突解决

This commit is contained in:
ray
2024-11-16 23:00:30 +08:00
parent 2dd4421fdf
commit 711e3ba5f4
48 changed files with 207 additions and 791 deletions

View File

@@ -30,8 +30,6 @@ const watermarkEnabled = computed(() => settingsStore.watermarkEnabled);
// 明亮/暗黑主题水印字体颜色适配
const fontColor = computed(() => {
return settingsStore.theme === ThemeEnum.DARK
? "rgba(255, 255, 255, .15)"
: "rgba(0, 0, 0, .15)";
return settingsStore.theme === ThemeEnum.DARK ? "rgba(255, 255, 255, .15)" : "rgba(0, 0, 0, .15)";
});
</script>

View File

@@ -1,13 +1,9 @@
<template>
<el-breadcrumb class="flex-y-center">
<transition-group
enter-active-class="animate__animated animate__fadeInRight"
>
<transition-group enter-active-class="animate__animated animate__fadeInRight">
<el-breadcrumb-item v-for="(item, index) in breadcrumbs" :key="item.path">
<span
v-if="
item.redirect === 'noredirect' || index === breadcrumbs.length - 1
"
v-if="item.redirect === 'noredirect' || index === breadcrumbs.length - 1"
class="color-gray-400"
>
{{ translateRouteTitle(item.meta.title) }}
@@ -36,14 +32,10 @@ const pathCompile = (path: string) => {
const breadcrumbs = ref<Array<RouteLocationMatched>>([]);
function getBreadcrumb() {
let matched = currentRoute.matched.filter(
(item) => item.meta && item.meta.title
);
let matched = currentRoute.matched.filter((item) => item.meta && item.meta.title);
const first = matched[0];
if (!isDashboard(first)) {
matched = [
{ path: "/dashboard", meta: { title: "dashboard" } } as any,
].concat(matched);
matched = [{ path: "/dashboard", meta: { title: "dashboard" } } as any].concat(matched);
}
breadcrumbs.value = matched.filter((item) => {
return item.meta && item.meta.title && item.meta.breadcrumb !== false;
@@ -55,10 +47,7 @@ function isDashboard(route: RouteLocationMatched) {
if (!name) {
return false;
}
return (
name.toString().trim().toLocaleLowerCase() ===
"Dashboard".toLocaleLowerCase()
);
return name.toString().trim().toLocaleLowerCase() === "Dashboard".toLocaleLowerCase();
}
function handleLink(item: any) {

View File

@@ -71,12 +71,7 @@
<template v-if="typeof item === 'string'">
<!-- 刷新 -->
<template v-if="item === 'refresh'">
<el-button
icon="refresh"
circle
title="刷新"
@click="handleToolbar(item)"
/>
<el-button icon="refresh" circle title="刷新" @click="handleToolbar(item)" />
</template>
<!-- 筛选列 -->
<template v-else-if="item === 'filter'">
@@ -86,11 +81,7 @@
</template>
<el-scrollbar max-height="350px">
<template v-for="col in cols" :key="col">
<el-checkbox
v-if="col.prop"
v-model="col.show"
:label="col.label"
/>
<el-checkbox v-if="col.prop" v-model="col.show" :label="col.label" />
</template>
</el-scrollbar>
</el-popover>
@@ -166,10 +157,7 @@
<template v-if="col.templet === 'image'">
<template v-if="col.prop">
<template v-if="Array.isArray(scope.row[col.prop])">
<template
v-for="(item, index) in scope.row[col.prop]"
:key="item"
>
<template v-for="(item, index) in scope.row[col.prop]" :key="item">
<el-image
:src="item"
:preview-src-list="scope.row[col.prop]"
@@ -198,11 +186,7 @@
<!-- 格式化显示链接 -->
<template v-else-if="col.templet === 'url'">
<template v-if="col.prop">
<el-link
type="primary"
:href="scope.row[col.prop]"
target="_blank"
>
<el-link type="primary" :href="scope.row[col.prop]" target="_blank">
{{ scope.row[col.prop] }}
</el-link>
</template>
@@ -221,8 +205,7 @@
:validate-event="false"
:disabled="!hasAuth(`${contentConfig.pageName}:modify`)"
@change="
pageData.length > 0 &&
handleModify(col.prop, scope.row[col.prop], scope.row)
pageData.length > 0 && handleModify(col.prop, scope.row[col.prop], scope.row)
"
/>
</template>
@@ -253,9 +236,7 @@
<template v-if="col.prop">
<template v-if="scope.row[col.prop].startsWith('el-icon-')">
<el-icon>
<component
:is="scope.row[col.prop].replace('el-icon-', '')"
/>
<component :is="scope.row[col.prop].replace('el-icon-', '')" />
</el-icon>
</template>
<template v-else>
@@ -268,20 +249,15 @@
<template v-if="col.prop">
{{
scope.row[col.prop]
? useDateFormat(
scope.row[col.prop],
col.dateFormat ?? "YYYY-MM-DD HH:mm:ss"
).value
? useDateFormat(scope.row[col.prop], col.dateFormat ?? "YYYY-MM-DD HH:mm:ss")
.value
: ""
}}
</template>
</template>
<!-- 列操作栏 -->
<template v-else-if="col.templet === 'tool'">
<template
v-for="item in col.operat ?? ['edit', 'delete']"
:key="item"
>
<template v-for="item in col.operat ?? ['edit', 'delete']" :key="item">
<template v-if="typeof item === 'string'">
<!-- 编辑/删除 -->
<template v-if="item === 'edit' || item === 'delete'">
@@ -329,11 +305,7 @@
</template>
<!-- 自定义 -->
<template v-else-if="col.templet === 'custom'">
<slot
:name="col.slotName ?? col.prop"
:prop="col.prop"
v-bind="scope"
/>
<slot :name="col.slotName ?? col.prop" :prop="col.prop" v-bind="scope" />
</template>
</template>
</el-table-column>
@@ -378,10 +350,7 @@
</el-form-item>
<el-form-item label="数据源" prop="origin">
<el-select v-model="exportsFormData.origin">
<el-option
label="当前数据 (当前页的数据)"
:value="ExportsOriginEnum.CURRENT"
/>
<el-option label="当前数据 (当前页的数据)" :value="ExportsOriginEnum.CURRENT" />
<el-option
label="选中数据 (所有选中的数据)"
:value="ExportsOriginEnum.SELECTED"
@@ -397,11 +366,7 @@
<el-form-item label="字段" prop="fields">
<el-checkbox-group v-model="exportsFormData.fields">
<template v-for="col in cols" :key="col">
<el-checkbox
v-if="col.prop"
:value="col.prop"
:label="col.label"
/>
<el-checkbox v-if="col.prop" :value="col.prop" :label="col.label" />
</template>
</el-checkbox-group>
</el-form-item>
@@ -410,9 +375,7 @@
<!-- 弹窗底部操作按钮 -->
<template #footer>
<div style="padding-right: var(--el-dialog-padding-primary)">
<el-button type="primary" @click="handleExportsSubmit">
</el-button>
<el-button type="primary" @click="handleExportsSubmit"> </el-button>
<el-button @click="handleCloseExportsModal"> </el-button>
</div>
</template>
@@ -524,10 +487,7 @@ const pk = props.contentConfig.pk ?? "id";
// 表格左侧工具栏
const toolbar = props.contentConfig.toolbar ?? ["add", "delete"];
// 表格右侧工具栏
const defaultToolbar = props.contentConfig.defaultToolbar ?? [
"refresh",
"filter",
];
const defaultToolbar = props.contentConfig.defaultToolbar ?? ["refresh", "filter"];
// 表格列
const cols = ref(
props.contentConfig.cols.map((col) => {
@@ -535,11 +495,7 @@ const cols = ref(
if (col.show === undefined) {
col.show = true;
}
if (
col.prop !== undefined &&
col.columnKey === undefined &&
col["column-key"] === undefined
) {
if (col.prop !== undefined && col.columnKey === undefined && col["column-key"] === undefined) {
col.columnKey = col.prop;
}
if (
@@ -672,9 +628,7 @@ function handleExports() {
const filename = exportsFormData.filename
? exportsFormData.filename
: props.contentConfig.pageName;
const sheetname = exportsFormData.sheetname
? exportsFormData.sheetname
: "sheet";
const sheetname = exportsFormData.sheetname ? exportsFormData.sheetname : "sheet";
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet(sheetname);
const columns: Partial<ExcelJS.Column>[] = [];
@@ -700,9 +654,7 @@ function handleExports() {
}
} else {
worksheet.addRows(
exportsFormData.origin === ExportsOriginEnum.SELECTED
? selectionData.value
: pageData.value
exportsFormData.origin === ExportsOriginEnum.SELECTED ? selectionData.value : pageData.value
);
workbook.xlsx
.writeBuffer()
@@ -821,11 +773,7 @@ function handleImports() {
fields.push(cell.value);
});
// 遍历工作表的每一行(从第二行开始,因为第一行通常是标题行)
for (
let rowNumber = 2;
rowNumber <= worksheet.rowCount;
rowNumber++
) {
for (let rowNumber = 2; rowNumber <= worksheet.rowCount; rowNumber++) {
const rowData: IObject = {};
const row = worksheet.getRow(rowNumber);
// 遍历当前行的每个单元格
@@ -903,11 +851,7 @@ function handleOperat(data: IOperatData) {
}
// 属性修改
function handleModify(
field: string,
value: boolean | string | number,
row: Record<string, any>
) {
function handleModify(field: string, value: boolean | string | number, row: Record<string, any>) {
if (props.contentConfig.modifyAction) {
props.contentConfig.modifyAction({
[pk]: row[pk],

View File

@@ -1,11 +1,5 @@
<template>
<el-form
ref="formRef"
label-width="auto"
v-bind="form"
:model="formData"
:rules="formRules"
>
<el-form ref="formRef" label-width="auto" v-bind="form" :model="formData" :rules="formRules">
<el-row :gutter="20">
<template v-for="item in formItems" :key="item.prop">
<el-col v-show="!item.hidden" v-bind="item.col">
@@ -48,10 +42,7 @@
</template>
<!-- Checkbox 多选框 -->
<template v-else-if="item.type === 'checkbox'">
<el-checkbox-group
v-model="formData[item.prop]"
v-bind="item.attrs"
>
<el-checkbox-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-checkbox v-bind="option" />
</template>
@@ -59,24 +50,15 @@
</template>
<!-- Input Number 数字输入框 -->
<template v-else-if="item.type === 'input-number'">
<el-input-number
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-input-number v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-tree-select v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-date-picker v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- Text 文本 -->
<template v-else-if="item.type === 'text'">

View File

@@ -36,78 +36,48 @@
</span>
</template>
<!-- Input 输入框 -->
<template
v-if="item.type === 'input' || item.type === undefined"
>
<template v-if="item.type === 'input' || item.type === undefined">
<el-input v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- Select 选择器 -->
<template v-else-if="item.type === 'select'">
<el-select v-model="formData[item.prop]" v-bind="item.attrs">
<template
v-for="option in item.options"
:key="option.value"
>
<template v-for="option in item.options" :key="option.value">
<el-option v-bind="option" />
</template>
</el-select>
</template>
<!-- Radio 单选框 -->
<template v-else-if="item.type === 'radio'">
<el-radio-group
v-model="formData[item.prop]"
v-bind="item.attrs"
>
<template
v-for="option in item.options"
:key="option.value"
>
<el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-radio v-bind="option" />
</template>
</el-radio-group>
</template>
<!-- switch 开关 -->
<template v-else-if="item.type === 'switch'">
<el-switch
v-model="formData[item.prop]"
inline-prompt
v-bind="item.attrs"
/>
<el-switch v-model="formData[item.prop]" inline-prompt v-bind="item.attrs" />
</template>
<!-- Checkbox 多选框 -->
<template v-else-if="item.type === 'checkbox'">
<el-checkbox-group
v-model="formData[item.prop]"
v-bind="item.attrs"
>
<template
v-for="option in item.options"
:key="option.value"
>
<el-checkbox-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-checkbox v-bind="option" />
</template>
</el-checkbox-group>
</template>
<!-- Input Number 数字输入框 -->
<template v-else-if="item.type === 'input-number'">
<el-input-number
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-input-number v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-tree-select v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-date-picker v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- Text 文本 -->
<template v-else-if="item.type === 'text'">
@@ -181,84 +151,48 @@
</span>
</template>
<!-- Input 输入框 -->
<template
v-if="item.type === 'input' || item.type === undefined"
>
<el-input
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<template v-if="item.type === 'input' || item.type === undefined">
<el-input v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- Select 选择器 -->
<template v-else-if="item.type === 'select'">
<el-select
v-model="formData[item.prop]"
v-bind="item.attrs"
>
<template
v-for="option in item.options"
:key="option.value"
>
<el-select v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-option v-bind="option" />
</template>
</el-select>
</template>
<!-- Radio 单选框 -->
<template v-else-if="item.type === 'radio'">
<el-radio-group
v-model="formData[item.prop]"
v-bind="item.attrs"
>
<template
v-for="option in item.options"
:key="option.value"
>
<el-radio-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-radio v-bind="option" />
</template>
</el-radio-group>
</template>
<!-- switch 开关 -->
<template v-else-if="item.type === 'switch'">
<el-switch
v-model="formData[item.prop]"
inline-prompt
v-bind="item.attrs"
/>
<el-switch v-model="formData[item.prop]" inline-prompt v-bind="item.attrs" />
</template>
<!-- Checkbox 多选框 -->
<template v-else-if="item.type === 'checkbox'">
<el-checkbox-group
v-model="formData[item.prop]"
v-bind="item.attrs"
>
<template
v-for="option in item.options"
:key="option.value"
>
<el-checkbox-group v-model="formData[item.prop]" v-bind="item.attrs">
<template v-for="option in item.options" :key="option.value">
<el-checkbox v-bind="option" />
</template>
</el-checkbox-group>
</template>
<!-- Input Number 数字输入框 -->
<template v-else-if="item.type === 'input-number'">
<el-input-number
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-input-number v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-tree-select v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker
v-model="formData[item.prop]"
v-bind="item.attrs"
/>
<el-date-picker v-model="formData[item.prop]" v-bind="item.attrs" />
</template>
<!-- Text 文本 -->
<template v-else-if="item.type === 'text'">

View File

@@ -51,9 +51,7 @@
</el-tag>
<template v-if="inputTagMap[item.prop].inputVisible">
<el-input
:ref="
(el: HTMLElement) => (inputTagMap[item.prop].inputRef = el)
"
:ref="(el: HTMLElement) => (inputTagMap[item.prop].inputRef = el)"
v-model="inputTagMap[item.prop].inputValue"
v-bind="inputTagMap[item.prop].inputAttrs"
@keyup.enter="handleInputConfirm(item.prop)"
@@ -80,24 +78,16 @@
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select
v-model="queryParams[item.prop]"
v-bind="item.attrs"
/>
<el-tree-select v-model="queryParams[item.prop]" v-bind="item.attrs" />
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker
v-model="queryParams[item.prop]"
v-bind="item.attrs"
/>
<el-date-picker v-model="queryParams[item.prop]" v-bind="item.attrs" />
</template>
</el-form-item>
</template>
<el-form-item>
<el-button type="primary" icon="search" @click="handleQuery">
搜索
</el-button>
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
<el-button icon="refresh" @click="handleReset">重置</el-button>
<!-- 展开/收起 -->
<el-link

View File

@@ -1,10 +1,5 @@
import { ref } from "vue";
import type {
IObject,
PageContentInstance,
PageModalInstance,
PageSearchInstance,
} from "./types";
import type { IObject, PageContentInstance, PageModalInstance, PageSearchInstance } from "./types";
function usePage() {
const searchRef = ref<PageSearchInstance>();

View File

@@ -21,9 +21,7 @@ const props = defineProps({
});
const label = ref("");
const tagType = ref<
"success" | "warning" | "info" | "primary" | "danger" | undefined
>();
const tagType = ref<"success" | "warning" | "info" | "primary" | "danger" | undefined>();
const tagSize = ref(props.size as "default" | "large" | "small");
@@ -41,18 +39,9 @@ const getLabelAndTagByValue = async (dictCode: string, value: any) => {
// 监听 props 的变化,获取并更新 label 和 tag
const fetchLabelAndTag = async () => {
const result = await getLabelAndTagByValue(
props.code as string,
props.modelValue
);
const result = await getLabelAndTagByValue(props.code as string, props.modelValue);
label.value = result.label;
tagType.value = result.tag as
| "success"
| "warning"
| "info"
| "primary"
| "danger"
| undefined;
tagType.value = result.tag as "success" | "warning" | "info" | "primary" | "danger" | undefined;
};
// 首次挂载时获取字典数据

View File

@@ -68,8 +68,7 @@ const props = defineProps({
type: {
type: String,
default: "select",
validator: (value: string) =>
["select", "radio", "checkbox"].includes(value),
validator: (value: string) => ["select", "radio", "checkbox"].includes(value),
},
placeholder: {
type: String,
@@ -120,9 +119,7 @@ watch(
(newOptions) => {
// options 加载后,确保 selectedValue 可以正确匹配到 options
if (newOptions.length > 0 && selectedValue.value !== undefined) {
const matchedOption = newOptions.find(
(option) => option.value === selectedValue.value
);
const matchedOption = newOptions.find((option) => option.value === selectedValue.value);
if (!matchedOption && props.type !== "checkbox") {
// 如果找不到匹配项,清空选中
selectedValue.value = "";

View File

@@ -4,10 +4,7 @@
class="px-[15px] flex items-center justify-center color-[var(--el-text-color-regular)]"
@click="toggleClick"
>
<svg-icon
icon-class="collapse"
:class="{ hamburger: true, 'is-active': isActive }"
/>
<svg-icon icon-class="collapse" :class="{ hamburger: true, 'is-active': isActive }" />
</div>
</template>

View File

@@ -1,19 +1,10 @@
<template>
<div ref="iconSelectRef" :style="{ width: props.width }">
<el-popover
:visible="popoverVisible"
:width="props.width"
placement="bottom-end"
>
<el-popover :visible="popoverVisible" :width="props.width" placement="bottom-end">
<template #reference>
<div @click="popoverVisible = !popoverVisible">
<slot>
<el-input
v-model="selectedIcon"
readonly
placeholder="点击选择图标"
class="reference"
>
<el-input v-model="selectedIcon" readonly placeholder="点击选择图标" class="reference">
<template #prepend>
<!-- 根据图标类型展示 -->
<el-icon v-if="isElementIcon">
@@ -49,12 +40,7 @@
<!-- 图标选择弹窗 -->
<div ref="popoverContentRef">
<el-input
v-model="filterText"
placeholder="搜索图标"
clearable
@input="filterIcons"
/>
<el-input v-model="filterText" placeholder="搜索图标" clearable @input="filterIcons" />
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
<el-tab-pane label="SVG 图标" name="svg">
<el-scrollbar height="300px">
@@ -147,9 +133,7 @@ function handleTabClick(tabPane: any) {
function filterIcons() {
if (activeTab.value === "svg") {
filteredSvgIcons.value = filterText.value
? svgIcons.value.filter((icon) =>
icon.toLowerCase().includes(filterText.value.toLowerCase())
)
? svgIcons.value.filter((icon) => icon.toLowerCase().includes(filterText.value.toLowerCase()))
: svgIcons.value;
} else {
filteredElementIcons.value = filterText.value
@@ -184,9 +168,7 @@ function clearSelectedIcon() {
onMounted(() => {
loadIcons();
if (selectedIcon.value) {
if (
elementIcons.value.includes(selectedIcon.value.replace("el-icon-", ""))
) {
if (elementIcons.value.includes(selectedIcon.value.replace("el-icon-", ""))) {
activeTab.value = "element";
} else {
activeTab.value = "svg";

View File

@@ -130,14 +130,10 @@ function navigateResults(direction: string) {
if (direction === "up") {
activeIndex.value =
activeIndex.value <= 0
? displayResults.value.length - 1
: activeIndex.value - 1;
activeIndex.value <= 0 ? displayResults.value.length - 1 : activeIndex.value - 1;
} else if (direction === "down") {
activeIndex.value =
activeIndex.value >= displayResults.value.length - 1
? 0
: activeIndex.value + 1;
activeIndex.value >= displayResults.value.length - 1 ? 0 : activeIndex.value + 1;
}
}
@@ -153,24 +149,19 @@ function navigateToRoute(item: SearchItem) {
function loadRoutes(routes: RouteRecordRaw[], parentPath = "") {
routes.forEach((route) => {
const path = route.path.startsWith("/")
? route.path
: `${parentPath}/${route.path}`;
if (excludedRoutes.value.includes(route.path) || isExternal(route.path))
return;
const path = route.path.startsWith("/") ? route.path : `${parentPath}/${route.path}`;
if (excludedRoutes.value.includes(route.path) || isExternal(route.path)) return;
if (route.children) {
loadRoutes(route.children, path);
} else if (route.meta?.title) {
const title =
route.meta.title === "dashboard" ? "首页" : route.meta.title;
const title = route.meta.title === "dashboard" ? "首页" : route.meta.title;
menuItems.value.push({
title,
path,
name: typeof route.name === "string" ? route.name : undefined,
icon: route.meta.icon,
redirect:
typeof route.redirect === "string" ? route.redirect : undefined,
redirect: typeof route.redirect === "string" ? route.redirect : undefined,
});
}
});

View File

@@ -1,10 +1,6 @@
<template>
<!-- 布局大小 -->
<el-tooltip
:content="$t('sizeSelect.tooltip')"
effect="dark"
placement="bottom"
>
<el-tooltip :content="$t('sizeSelect.tooltip')" effect="dark" placement="bottom">
<el-dropdown trigger="click" @command="handleSizeChange">
<div>
<svg-icon icon-class="size" />

View File

@@ -1,9 +1,5 @@
<template>
<svg
aria-hidden="true"
class="svg-icon"
:style="'width:' + size + ';height:' + size"
>
<svg aria-hidden="true" class="svg-icon" :style="'width:' + size + ';height:' + size">
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>

View File

@@ -63,17 +63,11 @@
</template>
<!-- TreeSelect 树形选择 -->
<template v-else-if="item.type === 'tree-select'">
<el-tree-select
v-model="queryParams[item.prop]"
v-bind="item.attrs"
/>
<el-tree-select v-model="queryParams[item.prop]" v-bind="item.attrs" />
</template>
<!-- DatePicker 日期选择器 -->
<template v-else-if="item.type === 'date-picker'">
<el-date-picker
v-model="queryParams[item.prop]"
v-bind="item.attrs"
/>
<el-date-picker v-model="queryParams[item.prop]" v-bind="item.attrs" />
</template>
<!-- Input 输入框 -->
<template v-else>
@@ -95,9 +89,7 @@
</el-form-item>
</template>
<el-form-item>
<el-button type="primary" icon="search" @click="handleQuery">
搜索
</el-button>
<el-button type="primary" icon="search" @click="handleQuery">搜索</el-button>
<el-button icon="refresh" @click="handleReset">重置</el-button>
</el-form-item>
</el-form>
@@ -119,11 +111,7 @@
<template v-if="col.templet === 'custom'">
<el-table-column v-bind="col">
<template #default="scope">
<slot
:name="col.slotName ?? col.prop"
:prop="col.prop"
v-bind="scope"
/>
<slot :name="col.slotName ?? col.prop" :prop="col.prop" v-bind="scope" />
</template>
</el-table-column>
</template>
@@ -294,9 +282,7 @@ for (const item of props.selectConfig.tableColumns) {
// 选择
const selectedItems = ref<IObject[]>([]);
const confirmText = computed(() => {
return selectedItems.value.length > 0
? `已选(${selectedItems.value.length})`
: "确 定";
return selectedItems.value.length > 0 ? `已选(${selectedItems.value.length})` : "确 定";
});
function handleSelect(selection: any[], row: any) {
if (isMultiple || selection.length === 0) {

View File

@@ -35,11 +35,7 @@
<a class="el-upload-list__item-name" @click="downloadFile(file)">
<el-icon><Document /></el-icon>
<span class="el-upload-list__item-file-name">{{ file.name }}</span>
<span
v-if="props.showDelBtn"
class="el-icon--close"
@click.stop="handleRemove(file)"
>
<span v-if="props.showDelBtn" class="el-icon--close" @click.stop="handleRemove(file)">
<el-icon><Close /></el-icon>
</span>
</a>
@@ -278,9 +274,7 @@ function handleRemove(removeFile: UploadUserFile) {
if (filePath) {
FileAPI.deleteByPath(filePath).then(() => {
// 删除成功回调
valFileList.value = valFileList.value.filter(
(file) => file.url !== filePath
);
valFileList.value = valFileList.value.filter((file) => file.url !== filePath);
emit("update:modelValue", valFileList.value);
});
}

View File

@@ -3,9 +3,7 @@
<el-upload
v-model:file-list="fileList"
list-type="picture-card"
:class="
fileList.length >= props.limit || !props.showUploadBtn ? 'hide' : 'show'
"
:class="fileList.length >= props.limit || !props.showUploadBtn ? 'hide' : 'show'"
:before-upload="handleBeforeUpload"
:action="props.action"
:headers="props.headers"
@@ -45,12 +43,7 @@
/>
</template>
<script setup lang="ts">
import {
UploadRawFile,
UploadUserFile,
UploadFile,
UploadProps,
} from "element-plus";
import { UploadRawFile, UploadUserFile, UploadFile, UploadProps } from "element-plus";
import FileAPI from "@/api/file";
import { getToken } from "@/utils/auth";
import { ResultEnum } from "@/enums/ResultEnum";
@@ -224,9 +217,7 @@ function handleBeforeUpload(file: UploadRawFile) {
*/
const previewImg: UploadProps["onPreview"] = (uploadFile: UploadFile) => {
viewFileList.value = fileList.value.map((file) => file.url!);
initialIndex.value = fileList.value.findIndex(
(file) => file.url === uploadFile.url
);
initialIndex.value = fileList.value.findIndex((file) => file.url === uploadFile.url);
viewVisible.value = true;
};

View File

@@ -30,11 +30,7 @@
</template>
<script setup lang="ts">
import {
ElImageViewer,
UploadRawFile,
UploadRequestOptions,
} from "element-plus";
import { ElImageViewer, UploadRawFile, UploadRequestOptions } from "element-plus";
import FileAPI from "@/api/file";
const props = defineProps({

View File

@@ -15,9 +15,7 @@ export const hasPerm: Directive = {
}
}
} else {
throw new Error(
"need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\""
);
throw new Error("need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\"");
}
},
};

View File

@@ -2,10 +2,7 @@
<section class="app-main" :style="{ height: appMainHeight }">
<router-view>
<template #default="{ Component, route }">
<transition
enter-active-class="animate__animated animate__fadeIn"
mode="out-in"
>
<transition enter-active-class="animate__animated animate__fadeIn" mode="out-in">
<keep-alive :include="cachedViews">
<component :is="Component" :key="route.path" />
</keep-alive>

View File

@@ -31,12 +31,7 @@
class="w400px flex-x-between p-1"
>
<div class="flex-center">
<DictLabel
v-model="item.type"
code="notice_type"
size="small"
class="mr-1"
/>
<DictLabel v-model="item.type" code="notice_type" size="small" class="mr-1" />
<el-text
type="primary"
size="small"
@@ -83,16 +78,8 @@
class="w400px flex-x-between p-1"
>
<div>
<DictLabel
v-model="item.type"
code="notice_type"
size="small"
/>
<el-link
type="primary"
class="ml-1"
@click="readNotice(item.id)"
>
<DictLabel v-model="item.type" code="notice_type" size="small" />
<el-link type="primary" class="ml-1" @click="readNotice(item.id)">
{{ item.title }}
</el-link>
</div>
@@ -127,22 +114,10 @@
<el-tab-pane label="待办" name="task">
<template v-if="tasks.length > 0">
<div
v-for="(item, index) in tasks"
:key="index"
class="w400px flex-x-between p-1"
>
<div v-for="(item, index) in tasks" :key="index" class="w400px flex-x-between p-1">
<div>
<DictLabel
v-model="item.type"
code="notice_type"
size="small"
/>
<el-link
type="primary"
class="ml-1"
@click="readNotice(item.id)"
>
<DictLabel v-model="item.type" code="notice_type" size="small" />
<el-link type="primary" class="ml-1" @click="readNotice(item.id)">
{{ item.title }}
</el-link>
</div>
@@ -196,11 +171,9 @@ const noticeDetailRef = ref();
// 获取未读消息列表并连接 WebSocket
onMounted(() => {
NoticeAPI.getMyNoticePage({ pageNum: 1, pageSize: 5, isRead: 0 }).then(
(data) => {
notices.value = data.list;
}
);
NoticeAPI.getMyNoticePage({ pageNum: 1, pageSize: 5, isRead: 0 }).then((data) => {
notices.value = data.list;
});
WebSocketManager.subscribeToTopic("/user/queue/message", (message) => {
console.log("收到消息:", message);

View File

@@ -1,10 +1,7 @@
<template>
<el-dropdown trigger="click">
<div class="flex-center h100% p13px">
<img
:src="userStore.userInfo.avatar"
class="rounded-full mr-10px w24px h24px"
/>
<img :src="userStore.userInfo.avatar" class="rounded-full mr-10px w24px h24px" />
<span>{{ userStore.userInfo.username }}</span>
</div>
<template #dropdown>
@@ -12,10 +9,7 @@
<el-dropdown-item @click="handleOpenUserProfile">
{{ $t("navbar.profile") }}
</el-dropdown-item>
<a
target="_blank"
href="https://gitee.com/youlaiorg/vue3-element-admin"
>
<a target="_blank" href="https://gitee.com/youlaiorg/vue3-element-admin">
<el-dropdown-item>{{ $t("navbar.gitee") }}</el-dropdown-item>
</a>
<a target="_blank" href="https://juejin.cn/post/7228990409909108793">

View File

@@ -1,28 +1,16 @@
<template>
<el-drawer
v-model="settingsVisible"
size="300"
:title="$t('settings.project')"
>
<el-drawer v-model="settingsVisible" size="300" :title="$t('settings.project')">
<el-divider>{{ $t("settings.theme") }}</el-divider>
<div class="flex-center">
<el-switch
v-model="isDark"
active-icon="Moon"
inactive-icon="Sunny"
@change="changeTheme"
/>
<el-switch v-model="isDark" active-icon="Moon" inactive-icon="Sunny" @change="changeTheme" />
</div>
<el-divider>{{ $t("settings.interface") }}</el-divider>
<div class="py-1 flex-x-between">
<span class="text-xs">{{ $t("settings.themeColor") }}</span>
<ThemeColorPicker
v-model="settingsStore.themeColor"
@update:model-value="changeThemeColor"
/>
<ThemeColorPicker v-model="settingsStore.themeColor" @update:model-value="changeThemeColor" />
</div>
<div class="py-1 flex-x-between">
@@ -47,10 +35,7 @@
<el-divider>{{ $t("settings.navigation") }}</el-divider>
<LayoutSelect
v-model="settingsStore.layout"
@update:model-value="changeLayout"
/>
<LayoutSelect v-model="settingsStore.layout" @update:model-value="changeLayout" />
</el-drawer>
</template>

View File

@@ -92,9 +92,7 @@ const onMenuOpen = (index: string) => {
* @param index 当前收起的菜单项索引
*/
const onMenuClose = (index: string) => {
expandedMenuIndexes.value = expandedMenuIndexes.value.filter(
(item) => item !== index
);
expandedMenuIndexes.value = expandedMenuIndexes.value.filter((item) => item !== index);
};
/**

View File

@@ -9,17 +9,10 @@
:active-text-color="variables['menu-active-text']"
@select="handleMenuSelect"
>
<el-menu-item
v-for="route in topMenus"
:key="route.path"
:index="route.path"
>
<el-menu-item v-for="route in topMenus" :key="route.path" :index="route.path">
<template #title>
<template v-if="route.meta && route.meta.icon">
<el-icon
v-if="route.meta.icon.startsWith('el-icon')"
class="sub-el-icon"
>
<el-icon v-if="route.meta.icon.startsWith('el-icon')" class="sub-el-icon">
<component :is="route.meta.icon.replace('el-icon-', '')" />
</el-icon>
<svg-icon v-else :icon-class="route.meta.icon" />
@@ -99,8 +92,6 @@ const navigateToFirstLeftMenu = (menus: RouteRecordRaw[]) => {
};
onMounted(() => {
topMenus.value = permissionStore.routes.filter(
(item) => !item.meta || !item.meta.hidden
);
topMenus.value = permissionStore.routes.filter((item) => !item.meta || !item.meta.hidden);
});
</script>

View File

@@ -1,10 +1,6 @@
<template>
<div class="tags-container">
<el-scrollbar
class="scroll-container"
:vertical="false"
@wheel.prevent="handleScroll"
>
<el-scrollbar class="scroll-container" :vertical="false" @wheel.prevent="handleScroll">
<router-link
v-for="tag in visitedViews"
ref="tagRef"
@@ -64,12 +60,7 @@ import { useRoute, useRouter, RouteRecordRaw } from "vue-router";
import { resolve } from "path-browserify";
import { translateRouteTitle } from "@/utils/i18n";
import {
usePermissionStore,
useTagsViewStore,
useSettingsStore,
useAppStore,
} from "@/store";
import { usePermissionStore, useTagsViewStore, useSettingsStore, useAppStore } from "@/store";
const { proxy } = getCurrentInstance()!;
const router = useRouter();

View File

@@ -1,9 +1,5 @@
import type { App } from "vue";
import {
createRouter,
createWebHashHistory,
type RouteRecordRaw,
} from "vue-router";
import { createRouter, createWebHashHistory, type RouteRecordRaw } from "vue-router";
export const Layout = () => import("@/layout/index.vue");
@@ -65,8 +61,7 @@ export const constantRoutes: RouteRecordRaw[] = [
{
path: "myNotice",
name: "MyNotice",
component: () =>
import("@/views/system/notice/components/MyNotice.vue"),
component: () => import("@/views/system/notice/components/MyNotice.vue"),
meta: { title: "我的通知", icon: "user", hidden: true },
},
],

View File

@@ -38,9 +38,7 @@ export const useAppStore = defineStore("app", () => {
// 切换侧边栏
function toggleSidebar() {
sidebar.opened = !sidebar.opened;
sidebarStatus.value = sidebar.opened
? SidebarStatusEnum.OPENED
: SidebarStatusEnum.CLOSED;
sidebarStatus.value = sidebar.opened ? SidebarStatusEnum.OPENED : SidebarStatusEnum.CLOSED;
}
// 关闭侧边栏

View File

@@ -10,15 +10,9 @@ export const useSettingsStore = defineStore("setting", () => {
// 标签
const tagsView = useStorage<boolean>("tagsView", defaultSettings.tagsView);
// 侧边栏 Logo
const sidebarLogo = useStorage<boolean>(
"sidebarLogo",
defaultSettings.sidebarLogo
);
const sidebarLogo = useStorage<boolean>("sidebarLogo", defaultSettings.sidebarLogo);
// 固定头部
const fixedHeader = useStorage<boolean>(
"fixedHeader",
defaultSettings.fixedHeader
);
const fixedHeader = useStorage<boolean>("fixedHeader", defaultSettings.fixedHeader);
// 布局
const layout = useStorage<string>("layout", defaultSettings.layout);
// 水印
@@ -28,10 +22,7 @@ export const useSettingsStore = defineStore("setting", () => {
);
// 主题
const themeColor = useStorage<string>(
"themeColor",
defaultSettings.themeColor
);
const themeColor = useStorage<string>("themeColor", defaultSettings.themeColor);
const theme = useStorage<string>("theme", defaultSettings.theme);
// 监听主题变化
@@ -53,13 +44,7 @@ export const useSettingsStore = defineStore("setting", () => {
watermarkEnabled,
};
function changeSetting({
key,
value,
}: {
key: string;
value: SettingsValue;
}) {
function changeSetting({ key, value }: { key: string; value: SettingsValue }) {
const setting = settingsMap[key];
if (setting) setting.value = value;
}

View File

@@ -124,9 +124,7 @@ export const useTagsViewStore = defineStore("tagsView", () => {
function delLeftViews(view: TagView) {
return new Promise((resolve) => {
const currIndex = visitedViews.value.findIndex(
(v) => v.path === view.path
);
const currIndex = visitedViews.value.findIndex((v) => v.path === view.path);
if (currIndex === -1) {
return;
}
@@ -149,9 +147,7 @@ export const useTagsViewStore = defineStore("tagsView", () => {
function delRightViews(view: TagView) {
return new Promise((resolve) => {
const currIndex = visitedViews.value.findIndex(
(v) => v.path === view.path
);
const currIndex = visitedViews.value.findIndex((v) => v.path === view.path);
if (currIndex === -1) {
return;
}

View File

@@ -3,8 +3,7 @@ import { getToken } from "@/utils/auth";
class WebSocketManager {
private client: Client | null = null;
private messageHandlers: Map<string, ((message: string) => void)[]> =
new Map();
private messageHandlers: Map<string, ((message: string) => void)[]> = new Map();
private reconnectAttempts = 0;
private maxReconnectAttempts = 3; // 自定义最大重试次数
private reconnectDelay = 5000; // 重试延迟(单位:毫秒)
@@ -15,9 +14,7 @@ class WebSocketManager {
// 如果没有配置 WebSocket 端点或显式关闭,直接返回
if (!endpoint) {
console.log(
"WebSocket 已被禁用,如需打开请在配置文件中配置 VITE_APP_WS_ENDPOINT"
);
console.log("WebSocket 已被禁用,如需打开请在配置文件中配置 VITE_APP_WS_ENDPOINT");
return;
}

View File

@@ -42,11 +42,7 @@
<el-table-column label="存储引擎" align="center" prop="engine" />
<el-table-column
label="排序规则"
align="center"
prop="tableCollation"
/>
<el-table-column label="排序规则" align="center" prop="tableCollation" />
<el-table-column label="创建时间" align="center" prop="createTime" />
<el-table-column fixed="right" label="操作" width="200">
@@ -115,10 +111,7 @@
</el-col>
<el-col :span="12">
<el-form-item label="业务名" prop="businessName">
<el-input
v-model="genConfigFormData.businessName"
placeholder="用户"
/>
<el-input v-model="genConfigFormData.businessName" placeholder="用户" />
</el-form-item>
</el-col>
</el-row>
@@ -126,18 +119,12 @@
<el-row>
<el-col :span="12">
<el-form-item label="包名" prop="packageName">
<el-input
v-model="genConfigFormData.packageName"
placeholder="com.youlai.boot"
/>
<el-input v-model="genConfigFormData.packageName" placeholder="com.youlai.boot" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="模块名" prop="moduleName">
<el-input
v-model="genConfigFormData.moduleName"
placeholder="system"
/>
<el-input v-model="genConfigFormData.moduleName" placeholder="system" />
</el-form-item>
</el-col>
</el-row>
@@ -145,18 +132,12 @@
<el-row>
<el-col :span="12">
<el-form-item label="实体名" prop="entityName">
<el-input
v-model="genConfigFormData.entityName"
placeholder="User"
/>
<el-input v-model="genConfigFormData.entityName" placeholder="User" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="作者">
<el-input
v-model="genConfigFormData.author"
placeholder="youlai"
/>
<el-input v-model="genConfigFormData.author" placeholder="youlai" />
</el-form-item>
</el-col>
</el-row>
@@ -256,11 +237,7 @@
</div>
</template>
<template #default="scope">
<el-checkbox
v-model="scope.row.isShowInQuery"
:true-value="1"
:false-value="0"
/>
<el-checkbox v-model="scope.row.isShowInQuery" :true-value="1" :false-value="0" />
</template>
</el-table-column>
@@ -277,11 +254,7 @@
</template>
<template #default="scope">
<el-checkbox
v-model="scope.row.isShowInList"
:true-value="1"
:false-value="0"
/>
<el-checkbox v-model="scope.row.isShowInList" :true-value="1" :false-value="0" />
</template>
</el-table-column>
@@ -298,11 +271,7 @@
</template>
<template #default="scope">
<el-checkbox
v-model="scope.row.isShowInForm"
:true-value="1"
:false-value="0"
/>
<el-checkbox v-model="scope.row.isShowInForm" :true-value="1" :false-value="0" />
</template>
</el-table-column>
@@ -339,10 +308,7 @@
<el-table-column label="表单类型" min-width="120">
<template #default="scope">
<el-select
v-if="
scope.row.isShowInQuery === 1 ||
scope.row.isShowInForm === 1
"
v-if="scope.row.isShowInQuery === 1 || scope.row.isShowInForm === 1"
v-model="scope.row.formType"
placeholder="请选择"
>
@@ -562,9 +528,7 @@ const initSort = () => {
if (sortFlag.value) {
return;
}
const table = document.querySelector(
".elTableCustom .el-table__body-wrapper tbody"
);
const table = document.querySelector(".elTableCustom .el-table__body-wrapper tbody");
sortFlag.value = Sortable.create(<HTMLElement>table, {
group: "shared",
animation: 150,
@@ -622,13 +586,7 @@ function handleNextClick() {
//这里需要校验基础配置
const { tableName, packageName, businessName, moduleName, entityName } =
genConfigFormData.value;
if (
!tableName ||
!packageName ||
!businessName ||
!moduleName ||
!entityName
) {
if (!tableName || !packageName || !businessName || !moduleName || !entityName) {
ElMessage.error("表名、业务名、包名、模块名、实体名不能为空");
return;
}
@@ -755,9 +713,7 @@ const toggleCheckAll = (key: FieldConfigKey, value: boolean) => {
const checkAllSelected = (key: keyof FieldConfig, isCheckAllRef: any) => {
const fieldConfigs = genConfigFormData.value?.fieldConfigs || [];
isCheckAllRef.value = fieldConfigs.every(
(row: FieldConfig) => row[key] === 1
);
isCheckAllRef.value = fieldConfigs.every((row: FieldConfig) => row[key] === 1);
};
/** 获取生成预览 */
@@ -787,9 +743,7 @@ function handlePreview(tableName: string) {
* @param data - 数据数组
* @returns 树形结构根节点
*/
function buildTree(
data: { path: string; fileName: string; content: string }[]
): TreeNode {
function buildTree(data: { path: string; fileName: string; content: string }[]): TreeNode {
// 动态获取根节点
const root: TreeNode = { label: "前后端代码", children: [] };
@@ -804,11 +758,10 @@ function buildTree(
"java",
genConfigFormData.value.backendAppName,
genConfigFormData.value.frontendAppName,
(
genConfigFormData.value.packageName +
"." +
genConfigFormData.value.moduleName
).replace(/\./g, separator),
(genConfigFormData.value.packageName + "." + genConfigFormData.value.moduleName).replace(
/\./g,
separator
),
];
// 检查路径中的特殊部分并合并它们

View File

@@ -16,11 +16,7 @@
</el-tooltip>
</div>
<el-radio-group
v-model="dataRange"
size="small"
@change="handleDateRangeChange"
>
<el-radio-group v-model="dataRange" size="small" @change="handleDateRangeChange">
<el-radio-button label="近7天" :value="1" />
<el-radio-button label="近30天" :value="2" />
</el-radio-group>
@@ -209,9 +205,7 @@ const handleResize = () => {
};
/** 初始化图表 */
onMounted(() => {
chart.value = markRaw(
echarts.init(document.getElementById(props.id) as HTMLDivElement)
);
chart.value = markRaw(echarts.init(document.getElementById(props.id) as HTMLDivElement));
loadData();
window.addEventListener("resize", handleResize);

View File

@@ -12,20 +12,14 @@
/>
<div>
<p>{{ greetings }}</p>
<p class="text-sm text-gray">
今日天气晴朗气温在15至25之间东南风
</p>
<p class="text-sm text-gray">今日天气晴朗气温在15至25之间东南风</p>
</div>
</div>
</el-col>
<el-col :span="6" :xs="24">
<div class="flex h-full items-center justify-around">
<el-statistic
v-for="item in statisticData"
:key="item.key"
:value="item.value"
>
<el-statistic v-for="item in statisticData" :key="item.key" :value="item.value">
<template #title>
<div class="flex items-center">
<svg-icon :icon-class="item.iconClass" size="20px" />
@@ -45,9 +39,7 @@
<el-card shadow="never">
<template #header>
<div class="flex-x-between">
<span class="text-[var(--el-text-color-secondary)]">
在线用户
</span>
<span class="text-[var(--el-text-color-secondary)]">在线用户</span>
<el-tag type="success" size="small">-</el-tag>
</div>
</template>
@@ -56,41 +48,27 @@
<span class="text-lg">{{ onlineUserCount }}</span>
<svg-icon icon-class="user" size="2em" />
</div>
<div
class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]"
>
<div class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]">
<span>总用户数</span>
<span>5</span>
</div>
</el-card>
</el-col>
<el-col
v-for="(item, index) in visitStatsList"
:key="index"
:xs="24"
:sm="12"
:lg="6"
>
<el-col v-for="(item, index) in visitStatsList" :key="index" :xs="24" :sm="12" :lg="6">
<el-skeleton :loading="visitStatsLoading" :rows="5" animated>
<template #template>
<el-card>
<template #header>
<div>
<el-skeleton-item variant="h3" style="width: 40%" />
<el-skeleton-item
variant="rect"
style="float: right; width: 1em; height: 1em"
/>
<el-skeleton-item variant="rect" style="float: right; width: 1em; height: 1em" />
</div>
</template>
<div class="flex-x-between">
<el-skeleton-item variant="text" style="width: 30%" />
<el-skeleton-item
variant="circle"
style="width: 2em; height: 2em"
/>
<el-skeleton-item variant="circle" style="width: 2em; height: 2em" />
</div>
<div class="mt-5 flex-x-between">
<el-skeleton-item variant="text" style="width: 50%" />
@@ -114,13 +92,7 @@
<div class="flex-x-between mt-2">
<div class="flex-y-center">
<span class="text-lg">{{ item.todayCount }}</span>
<span
:class="[
'text-xs',
'ml-2',
getGrowthRateClass(item.growthRate),
]"
>
<span :class="['text-xs', 'ml-2', getGrowthRateClass(item.growthRate)]">
<el-icon>
<Top v-if="item.growthRate > 0" />
<Bottom v-else-if="item.growthRate < 0" />
@@ -131,9 +103,7 @@
<svg-icon :icon-class="item.icon" size="2em" />
</div>
<div
class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]"
>
<div class="flex-x-between mt-2 text-sm text-[var(--el-text-color-secondary)]">
<span>{{ item.title }}</span>
<span>{{ item.totalCount }}</span>
</div>
@@ -161,11 +131,7 @@
</template>
<el-scrollbar height="400px">
<div
v-for="(item, index) in notices"
:key="index"
class="flex-y-center py-3"
>
<div v-for="(item, index) in notices" :key="index" class="flex-y-center py-3">
<DictLabel v-model="item.type" code="notice_type" size="small" />
<el-text
truncated
@@ -263,11 +229,7 @@ const loadVisitStatsData = async () => {
const list: VisitStatsVO[] = await StatsAPI.getVisitStats();
if (list) {
const tagTypes: ("primary" | "success" | "warning")[] = [
"primary",
"success",
"warning",
];
const tagTypes: ("primary" | "success" | "warning")[] = ["primary", "success", "warning"];
const transformedList: VisitStats[] = list.map((item, index) => ({
title: item.title,
icon: getVisitStatsIcon(item.type),

View File

@@ -1,12 +1,7 @@
<!-- 接口文档 -->
<template>
<div class="app-container">
<iframe
src="http://vapi.youlai.tech/doc.html"
width="100%"
height="100%"
frameborder="0"
/>
<iframe src="http://vapi.youlai.tech/doc.html" width="100%" height="100%" frameborder="0" />
</div>
</template>

View File

@@ -15,8 +15,7 @@ const contentConfig: IContentConfig = {
{
id: 1,
username: "tom",
avatar:
"https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
avatar: "https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
percent: 99,
price: 10,
url: "https://www.baidu.com",
@@ -30,8 +29,7 @@ const contentConfig: IContentConfig = {
{
id: 2,
username: "jerry",
avatar:
"https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
avatar: "https://foruda.gitee.com/images/1723603502796844527/03cdca2a_716974.gif",
percent: 88,
price: 999,
url: "https://www.google.com",

View File

@@ -8,9 +8,7 @@
>
示例源码 请点击>>>>
</el-link>
<el-button type="primary" plain round size="small" @click="isA = !isA">
切换示例
</el-button>
<el-button type="primary" plain round size="small" @click="isA = !isA">切换示例</el-button>
</div>
<!-- 列表 -->
@@ -146,14 +144,10 @@ function handleOperatClick(data: IOperatData) {
console.log(data);
// 重置密码
if (data.name === "reset_pwd") {
ElMessageBox.prompt(
"请输入用户「" + data.row.username + "」的新密码",
"重置密码",
{
confirmButtonText: "确定",
cancelButtonText: "取消",
}
).then(({ value }) => {
ElMessageBox.prompt("请输入用户「" + data.row.username + "」的新密码", "重置密码", {
confirmButtonText: "确定",
cancelButtonText: "取消",
}).then(({ value }) => {
if (!value || value.length < 6) {
ElMessage.warning("密码至少需要6位字符请重新输入");
return false;

View File

@@ -5,11 +5,7 @@
<div class="grid">
<div v-for="item of svg_icons" :key="item">
<copy-button :text="generateIconCode(item)">
<el-tooltip
effect="dark"
:content="generateIconCode(item)"
placement="top"
>
<el-tooltip effect="dark" :content="generateIconCode(item)" placement="top">
<div class="icon-item">
<svg-icon :icon-class="item" />
<span>{{ item }}</span>
@@ -23,11 +19,7 @@
<div class="grid">
<div v-for="(icon, name) of icons" :key="name">
<copy-button :text="generateElementIconCode(name)">
<el-tooltip
effect="dark"
:content="generateElementIconCode(name)"
placement="top"
>
<el-tooltip effect="dark" :content="generateElementIconCode(name)" placement="top">
<div class="icon-item">
<el-icon :size="20">
<component :is="icon" />

View File

@@ -36,11 +36,7 @@ const text = computed(() => {
>
示例源码 请点击>>>>
</el-link>
<table-select
:text="text"
:select-config="selectConfig"
@confirm-click="handleConfirm"
>
<table-select :text="text" :select-config="selectConfig" @confirm-click="handleConfirm">
<template #status="scope">
<el-tag :type="scope.row[scope.prop] == 1 ? 'success' : 'info'">
{{ scope.row[scope.prop] == 1 ? "启用" : "禁用" }}

View File

@@ -15,9 +15,6 @@ const value = ref("初始内容");
>
示例源码 请点击>>>>
</el-link>
<editor
v-model="value"
style="z-index: 99999; height: calc(100vh - 180px)"
/>
<editor v-model="value" style="z-index: 99999; height: calc(100vh - 180px)" />
</div>
</template>

View File

@@ -22,19 +22,13 @@
>
连接
</el-button>
<el-button
type="danger"
:disabled="!isConnected"
@click="disconnectWebSocket"
>
<el-button type="danger" :disabled="!isConnected" @click="disconnectWebSocket">
断开
</el-button>
</el-col>
<el-col :span="8" class="text-right">
连接状态
<el-tag v-if="isConnected" class="ml-2" type="success">
已连接
</el-tag>
<el-tag v-if="isConnected" class="ml-2" type="success">已连接</el-tag>
<el-tag v-else class="ml-2" type="info">已断开</el-tag>
</el-col>
</el-row>
@@ -60,9 +54,7 @@
<el-input v-model="receiver" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="sendToUser">
发送点对点消息
</el-button>
<el-button type="primary" @click="sendToUser">发送点对点消息</el-button>
</el-form-item>
</el-form>
</el-card>
@@ -78,17 +70,14 @@
'tip-message': message.type === 'tip',
message: message.type !== 'tip',
'message--sent': message.sender === userStore.userInfo.username,
'message--received':
message.sender !== userStore.userInfo.username,
'message--received': message.sender !== userStore.userInfo.username,
}"
>
<div v-if="message.type != 'tip'" class="message-content">
<div
:class="{
'message-sender':
message.sender === userStore.userInfo.username,
'message-receiver':
message.sender !== userStore.userInfo.username,
'message-sender': message.sender === userStore.userInfo.username,
'message-receiver': message.sender !== userStore.userInfo.username,
}"
>
{{ message.sender }}
@@ -124,16 +113,10 @@ interface MessageType {
const messages = ref<MessageType[]>([]);
const topicMessage = ref(
"亲爱的大冤种们由于一只史诗级的BUG系统版本已经被迫回退到了0.0.1。"
); // 广播消息
const topicMessage = ref("亲爱的大冤种们由于一只史诗级的BUG系统版本已经被迫回退到了0.0.1。"); // 广播消息
const queneMessage = ref(
"hi , " +
receiver.value +
" , 我是" +
userStore.userInfo.username +
" , 想和你交个朋友 ! "
"hi , " + receiver.value + " , 我是" + userStore.userInfo.username + " , 想和你交个朋友 ! "
);
let stompClient: Client;

View File

@@ -8,8 +8,7 @@ defineOptions({
const state = reactive({
errGif: new URL("../../assets/images/401.gif", import.meta.url).href,
ewizardClap:
"https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",
ewizardClap: "https://wpimg.wallstcn.com/007ef517-bafd-4066-aae4-6883632d9646",
dialogVisible: false,
});
@@ -24,9 +23,7 @@ function back() {
<template>
<div class="page-container">
<el-button icon="el-icon-arrow-left" class="pan-back-btn" @click="back">
返回
</el-button>
<el-button icon="el-icon-arrow-left" class="pan-back-btn" @click="back">返回</el-button>
<el-row>
<el-col :span="12">
<h1 class="text-jumbo text-ginormous">Oops!</h1>
@@ -49,12 +46,7 @@ function back() {
</ul>
</el-col>
<el-col :span="12">
<img
:src="errGif"
width="313"
height="428"
alt="Girl has dropped her ice cream."
/>
<img :src="errGif" width="313" height="428" alt="Girl has dropped her ice cream." />
</el-col>
</el-row>
<el-dialog v-model="dialogVisible" title="随便看">

View File

@@ -16,44 +16,22 @@ function back() {
<div class="page-container">
<div class="pic-404">
<img class="pic-404__parent" src="@/assets/images/404.png" alt="404" />
<img
class="pic-404__child left"
src="@/assets/images/404_cloud.png"
alt="404"
/>
<img
class="pic-404__child mid"
src="@/assets/images/404_cloud.png"
alt="404"
/>
<img
class="pic-404__child right"
src="@/assets/images/404_cloud.png"
alt="404"
/>
<img class="pic-404__child left" src="@/assets/images/404_cloud.png" alt="404" />
<img class="pic-404__child mid" src="@/assets/images/404_cloud.png" alt="404" />
<img class="pic-404__child right" src="@/assets/images/404_cloud.png" alt="404" />
</div>
<div class="bullshit">
<div class="bullshit__oops">OOPS!</div>
<div class="bullshit__info">
All rights reserved
<a
style="color: #20a0ff"
href="https://wallstreetcn.com"
target="_blank"
>
wallstreetcn
</a>
</div>
<div class="bullshit__headline">
The webmaster said that you can not enter this page...
<a style="color: #20a0ff" href="https://wallstreetcn.com" target="_blank">wallstreetcn</a>
</div>
<div class="bullshit__headline">The webmaster said that you can not enter this page...</div>
<div class="bullshit__info">
Please check that the URL you entered is correct, or click the button
below to return to the homepage.
Please check that the URL you entered is correct, or click the button below to return to the
homepage.
</div>
<a href="#" class="bullshit__return-home" @click.prevent="back">
Back to home
</a>
<a href="#" class="bullshit__return-home" @click.prevent="back">Back to home</a>
</div>
</div>
</template>

View File

@@ -38,19 +38,13 @@
</el-tag>
</el-dropdown-item>
<el-dropdown-item
@click="setLoginCredentials('root', '123456')"
>
<el-dropdown-item @click="setLoginCredentials('root', '123456')">
超级管理员root/123456
</el-dropdown-item>
<el-dropdown-item
@click="setLoginCredentials('admin', '123456')"
>
<el-dropdown-item @click="setLoginCredentials('admin', '123456')">
系统管理员admin/123456
</el-dropdown-item>
<el-dropdown-item
@click="setLoginCredentials('test', '123456')"
>
<el-dropdown-item @click="setLoginCredentials('test', '123456')">
测试小游客test/123456
</el-dropdown-item>
</el-dropdown-menu>
@@ -76,11 +70,7 @@
</el-form-item>
<!-- 密码 -->
<el-tooltip
:visible="isCapslock"
:content="$t('login.capsLock')"
placement="right"
>
<el-tooltip :visible="isCapslock" :content="$t('login.capsLock')" placement="right">
<el-form-item prop="password">
<div class="input-wrapper">
<el-icon class="mx-2">
@@ -114,11 +104,7 @@
@keyup.enter="handleLoginSubmit"
/>
<el-image
:src="captchaBase64"
class="captcha-img"
@click="getCaptcha"
/>
<el-image :src="captchaBase64" class="captcha-img" @click="getCaptcha" />
</div>
</el-form-item>
@@ -161,11 +147,7 @@
<div class="login-footer">
<el-text size="small">
Copyright © 2021 - 2024 youlai.tech All Rights Reserved.
<el-link
:underline="false"
href="http://beian.miit.gov.cn/"
target="_blank"
>
<el-link :underline="false" href="http://beian.miit.gov.cn/" target="_blank">
皖ICP备20006496号-2
</el-link>
</el-text>
@@ -200,9 +182,7 @@ const isCapslock = ref(false); // 是否大写锁定
const captchaBase64 = ref(); // 验证码图片Base64字符串
const logo = ref(new URL("../../assets/logo.png", import.meta.url).href);
const loginImage = ref(
new URL("../../assets/images/login-image.svg", import.meta.url).href
);
const loginImage = ref(new URL("../../assets/images/login-image.svg", import.meta.url).href);
const loginData = ref<LoginData>({
username: "admin",
@@ -300,8 +280,7 @@ function parseRedirect(): {
// 主题切换
const toggleTheme = () => {
const newTheme =
settingsStore.theme === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
const newTheme = settingsStore.theme === ThemeEnum.DARK ? ThemeEnum.LIGHT : ThemeEnum.DARK;
settingsStore.changeTheme(newTheme);
};
@@ -332,8 +311,7 @@ onMounted(() => {
width: 100%;
height: 100%;
overflow-y: auto;
background: url("@/assets/images/login-background-light.jpg") no-repeat center
right;
background: url("@/assets/images/login-background-light.jpg") no-repeat center right;
.login-header {
position: absolute;
@@ -471,8 +449,7 @@ onMounted(() => {
html.dark {
.login {
background: url("@/assets/images/login-background-dark.jpg") no-repeat
center right;
background: url("@/assets/images/login-background-dark.jpg") no-repeat center right;
.login-content {
background: transparent;

View File

@@ -16,12 +16,7 @@
size="small"
@click="triggerFileUpload"
/>
<input
ref="fileInput"
type="file"
style="display: none"
@change="handleFileChange"
/>
<input ref="fileInput" type="file" style="display: none" @change="handleFileChange" />
</div>
<div class="mt-5">
{{ userProfile.nickname }}
@@ -41,10 +36,7 @@
用户名
</template>
{{ userProfile.username }}
<el-icon
v-if="userProfile.gender === 1"
class="align-middle color-blue"
>
<el-icon v-if="userProfile.gender === 1" class="align-middle color-blue">
<Male />
</el-icon>
<el-icon v-else class="align-middle color-pink">
@@ -117,9 +109,7 @@
<div class="mt-5">
<div class="font-bold">绑定手机</div>
<div class="text-14px mt-2">
<span v-if="userProfile.mobile">
已绑定手机号{{ userProfile.mobile }}
</span>
<span v-if="userProfile.mobile">已绑定手机号{{ userProfile.mobile }}</span>
<span v-else>未绑定手机</span>
<el-button
v-if="userProfile.mobile"
@@ -147,9 +137,7 @@
<div class="mt-5">
<div class="font-bold">绑定邮箱</div>
<div class="text-14px mt-2">
<span v-if="userProfile.email">
已绑定邮箱{{ userProfile.email }}
</span>
<span v-if="userProfile.email">已绑定邮箱{{ userProfile.email }}</span>
<span v-else>未绑定邮箱</span>
<el-button
v-if="userProfile.email"
@@ -203,25 +191,13 @@
:label-width="100"
>
<el-form-item label="原密码" prop="oldPassword">
<el-input
v-model="passwordChangeForm.oldPassword"
type="password"
show-password
/>
<el-input v-model="passwordChangeForm.oldPassword" type="password" show-password />
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input
v-model="passwordChangeForm.newPassword"
type="password"
show-password
/>
<el-input v-model="passwordChangeForm.newPassword" type="password" show-password />
</el-form-item>
<el-form-item label="确认密码" prop="confirmPassword">
<el-input
v-model="passwordChangeForm.confirmPassword"
type="password"
show-password
/>
<el-input v-model="passwordChangeForm.confirmPassword" type="password" show-password />
</el-form-item>
</el-form>
<!-- 绑定手机 -->
@@ -243,11 +219,7 @@
:disabled="mobileCountdown > 0"
@click="handleSendVerificationCode('MOBILE')"
>
{{
mobileCountdown > 0
? `${mobileCountdown}s后重新发送`
: "发送验证码"
}}
{{ mobileCountdown > 0 ? `${mobileCountdown}s后重新发送` : "发送验证码" }}
</el-button>
</template>
</el-input>
@@ -273,11 +245,7 @@
:disabled="emailCountdown > 0"
@click="handleSendVerificationCode('EMAIL')"
>
{{
emailCountdown > 0
? `${emailCountdown}s后重新发送`
: "发送验证码"
}}
{{ emailCountdown > 0 ? `${emailCountdown}s后重新发送` : "发送验证码" }}
</el-button>
</template>
</el-input>
@@ -336,9 +304,7 @@ const emailTimer = ref<NodeJS.Timeout | null>(null);
const passwordChangeRules = {
oldPassword: [{ required: true, message: "请输入原密码", trigger: "blur" }],
newPassword: [{ required: true, message: "请输入新密码", trigger: "blur" }],
confirmPassword: [
{ required: true, message: "请再次输入新密码", trigger: "blur" },
],
confirmPassword: [{ required: true, message: "请再次输入新密码", trigger: "blur" }],
};
// 手机号校验规则

View File

@@ -31,7 +31,7 @@
v-hasPerm="['sys:dept:add']"
type="success"
icon="plus"
@click="handleOpenDialog(0, undefined)"
@click="handleOpenDialog()"
>
新增
</el-button>

View File

@@ -32,12 +32,8 @@
</el-descriptions-item>
<el-descriptions-item label="发布状态:">
<el-tag v-if="notice.publishStatus == 0" type="info">未发布</el-tag>
<el-tag v-else-if="notice.publishStatus == 1" type="success">
已发布
</el-tag>
<el-tag v-else-if="notice.publishStatus == -1" type="warning">
已撤回
</el-tag>
<el-tag v-else-if="notice.publishStatus == 1" type="success">已发布</el-tag>
<el-tag v-else-if="notice.publishStatus == -1" type="warning">已撤回</el-tag>
</el-descriptions-item>
<el-descriptions-item label="发布人:">
{{ notice.publisherName }}

View File

@@ -95,9 +95,7 @@ const handleFileExceed = () => {
const handleDownloadTemplate = () => {
UserAPI.downloadTemplate().then((response: any) => {
const fileData = response.data;
const fileName = decodeURI(
response.headers["content-disposition"].split(";")[1].split("=")[1]
);
const fileName = decodeURI(response.headers["content-disposition"].split(";")[1].split("=")[1]);
const fileType =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8";