chore: 🔨 合并冲突解决
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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'">
|
||||
|
||||
@@ -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'">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
// 首次挂载时获取字典数据
|
||||
|
||||
@@ -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 = "";
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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']\"");
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 },
|
||||
},
|
||||
],
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
// 关闭侧边栏
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
),
|
||||
];
|
||||
|
||||
// 检查路径中的特殊部分并合并它们
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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 ? "启用" : "禁用" }}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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="随便看">
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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" }],
|
||||
};
|
||||
|
||||
// 手机号校验规则
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
v-hasPerm="['sys:dept:add']"
|
||||
type="success"
|
||||
icon="plus"
|
||||
@click="handleOpenDialog(0, undefined)"
|
||||
@click="handleOpenDialog()"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
|
||||
@@ -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 }}
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
Reference in New Issue
Block a user