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(() => { const fontColor = computed(() => {
return settingsStore.theme === ThemeEnum.DARK return settingsStore.theme === ThemeEnum.DARK ? "rgba(255, 255, 255, .15)" : "rgba(0, 0, 0, .15)";
? "rgba(255, 255, 255, .15)"
: "rgba(0, 0, 0, .15)";
}); });
</script> </script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -15,9 +15,7 @@ export const hasPerm: Directive = {
} }
} }
} else { } else {
throw new Error( throw new Error("need perms! Like v-has-perm=\"['sys:user:add','sys:user:edit']\"");
"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 }"> <section class="app-main" :style="{ height: appMainHeight }">
<router-view> <router-view>
<template #default="{ Component, route }"> <template #default="{ Component, route }">
<transition <transition enter-active-class="animate__animated animate__fadeIn" mode="out-in">
enter-active-class="animate__animated animate__fadeIn"
mode="out-in"
>
<keep-alive :include="cachedViews"> <keep-alive :include="cachedViews">
<component :is="Component" :key="route.path" /> <component :is="Component" :key="route.path" />
</keep-alive> </keep-alive>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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