mirror of
https://github.com/fastapiadmin/FastapiAdmin.git
synced 2026-09-25 13:51:04 +00:00
feat(components): 增强 FaForm/FaDialog/FaDrawer 并重构业务页面
- FaForm 新增 scrollbar 和 maxHeight 属性,支持表单滚动条 - FaForm 暴露 resetFields、clearValidate、validateField 方法 - FaDialog 和 FaDrawer 新增 formMode、confirmLoading 等属性及 confirm/cancel 事件 - 使用 FaDescriptions 和 FaForm 重构 demo 和 memory 页面,减少模板冗余 - 修复布局组件路径引用,统一至 @/components/layouts - 补充 Fa 前缀组件的 ESLint 全局变量声明
This commit is contained in:
@@ -74,48 +74,24 @@
|
||||
width="920px"
|
||||
dialog-class="session-detail-dialog"
|
||||
modal-class="session-detail-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="会话ID" :span="2">
|
||||
{{ detailFormData.session_id }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="标题" :span="2">
|
||||
{{ detailFormData.title }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="用户ID" :span="1">
|
||||
{{ detailFormData.user_id }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="团队ID" :span="1">
|
||||
{{ detailFormData.team_id }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="部门名称" :span="1">
|
||||
{{ detailFormData.team_name || "未知部门" }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="Agent ID" :span="1">
|
||||
{{ detailFormData.agent_id }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="1">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="1">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="消息数量" :span="1">
|
||||
{{ detailFormData.message_count }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="会话摘要" :span="2">
|
||||
{{ detailFormData.summary || "无" }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="元数据" :span="2">
|
||||
<pre v-if="detailFormData.metadata">{{
|
||||
JSON.stringify(detailFormData.metadata, null, 2)
|
||||
}}</pre>
|
||||
<FaDescriptions
|
||||
:column="2"
|
||||
:data="detailFormData"
|
||||
:items="memoryDetailItems"
|
||||
:scrollbar="false"
|
||||
>
|
||||
<template #metadata="{ row }">
|
||||
<pre v-if="row?.metadata">{{ JSON.stringify(row?.metadata, null, 2) }}</pre>
|
||||
<span v-else>无</span>
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</template>
|
||||
</FaDescriptions>
|
||||
|
||||
<ElDivider content-position="left">消息记录</ElDivider>
|
||||
<ElTimeline v-if="detailFormData.messages && detailFormData.messages.length > 0">
|
||||
@@ -158,16 +134,6 @@
|
||||
class="crud-dialog-art-form"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</FaDialog>
|
||||
</div>
|
||||
</template>
|
||||
@@ -184,13 +150,7 @@ import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import AiChatAPI, { type ChatSession, type ChatSessionDetail } from "@/api/module_ai/chat";
|
||||
import { formatToDateTime } from "@utils/common";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
@@ -264,6 +224,7 @@ const memorySearchItems = computed<SearchFormItem[]>(() => [
|
||||
]);
|
||||
|
||||
const dataFormRef = ref<InstanceType<typeof FaForm> | null>(null);
|
||||
const submitLoading = ref(false);
|
||||
const memoryFormRenderKey = ref(0);
|
||||
|
||||
const memoryDialogFormItems = computed<FormItem[]>(() => [
|
||||
@@ -439,6 +400,21 @@ const dialogVisible = reactive({
|
||||
|
||||
const detailFormData = ref<Partial<ChatSessionDetail>>({});
|
||||
|
||||
const memoryDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "会话ID", prop: "session_id" },
|
||||
{ label: "标题", prop: "title" },
|
||||
{ label: "用户ID", prop: "user_id", span: 1 },
|
||||
{ label: "团队ID", prop: "team_id", span: 1 },
|
||||
{ label: "部门名称", prop: "team_name", span: 1 },
|
||||
{ label: "Agent ID", prop: "agent_id", span: 1 },
|
||||
{ label: "创建时间", prop: "created_time", span: 1 },
|
||||
{ label: "更新时间", prop: "updated_time", span: 1 },
|
||||
{ label: "消息数量", prop: "message_count", span: 1 },
|
||||
{ label: "会话摘要", prop: "summary" },
|
||||
{ label: "元数据", prop: "metadata", slot: "metadata" },
|
||||
];
|
||||
|
||||
const rules = reactive({
|
||||
title: [{ required: true, message: "请输入标题", trigger: "blur" }],
|
||||
});
|
||||
@@ -469,8 +445,8 @@ async function onResetSearch() {
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, initialFormData);
|
||||
}
|
||||
|
||||
|
||||
@@ -457,8 +457,8 @@ function resetForm() {
|
||||
status: "0",
|
||||
description: "",
|
||||
});
|
||||
formRef.value?.ref?.resetFields();
|
||||
formRef.value?.ref?.clearValidate();
|
||||
formRef.value?.resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
|
||||
function handleCloseDialog() {
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
:perm-patch="['module_example:demo:patch']"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="openEditDialog('add')"
|
||||
@import="openImportModal"
|
||||
@export="openExportModal"
|
||||
@import="openImport"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
@more="runBatchStatus"
|
||||
/>
|
||||
@@ -64,205 +64,86 @@
|
||||
width="920px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptionsItem label="名称" :span="2">
|
||||
{{ detailFormData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="UUID" :span="2">
|
||||
{{ detailFormData.uuid }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag :type="detailFormData.status === '0' ? 'success' : 'danger'">
|
||||
{{ detailFormData.status === "0" ? "启用" : "停用" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="整数" :span="2">
|
||||
{{ detailFormData.a }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="大整数" :span="2">
|
||||
{{ detailFormData.b }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="浮点数" :span="2">
|
||||
{{ detailFormData.c }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="布尔值" :span="2">
|
||||
<ElTag :type="detailFormData.d ? 'success' : 'danger'">
|
||||
{{ detailFormData.d ? "是" : "否" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="日期" :span="2">
|
||||
{{ detailFormData.e }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="时间" :span="2">
|
||||
{{ detailFormData.f }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="日期时间" :span="2">
|
||||
{{ detailFormData.g }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="长文本" :span="2">
|
||||
{{ detailFormData.h }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="元数据" :span="2">
|
||||
<JsonPretty
|
||||
v-if="detailFormData.i != null"
|
||||
:value="detailFormData.i"
|
||||
height="140px"
|
||||
/>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="2">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建人" :span="2">
|
||||
{{ detailFormData.created_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新人" :span="2">
|
||||
{{ detailFormData.updated_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="4"
|
||||
:data="detailFormData"
|
||||
:items="demoDetailItems"
|
||||
max-height="70vh"
|
||||
>
|
||||
<template #i="{ row }">
|
||||
<JsonPretty v-if="row?.i != null" :value="row?.i" height="140px" />
|
||||
</template>
|
||||
</FaDescriptions>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElForm
|
||||
ref="dataFormRef"
|
||||
:model="formData"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
label-width="100px"
|
||||
label-position="right"
|
||||
inline
|
||||
>
|
||||
<ElFormItem label="名称" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入名称" :maxlength="50" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="状态" prop="status">
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="整数" prop="a">
|
||||
<ElInputNumber v-model="formData.a" placeholder="请输入整数" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="大整数" prop="b">
|
||||
<ElInputNumber v-model="formData.b" placeholder="请输入大整数" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="浮点数" prop="c">
|
||||
<ElInputNumber
|
||||
v-model="formData.c"
|
||||
placeholder="请输入浮点数"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="布尔值" prop="d">
|
||||
<ElSwitch v-model="formData.d" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="日期" prop="e">
|
||||
<ElDatePicker
|
||||
v-model="formData.e"
|
||||
type="date"
|
||||
placeholder="请选择日期"
|
||||
:style="'width: 100%'"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="时间" prop="f">
|
||||
<ElTimePicker
|
||||
v-model="formData.f"
|
||||
placeholder="请选择时间"
|
||||
:style="'width: 100%'"
|
||||
value-format="HH:mm:ss"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="日期时间" prop="g">
|
||||
<ElDatePicker
|
||||
v-model="formData.g"
|
||||
type="datetime"
|
||||
placeholder="请选择日期时间"
|
||||
:style="'width: 100%'"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="长文本" prop="h">
|
||||
<ElInput v-model="formData.h" :rows="4" type="textarea" placeholder="请输入长文本" />
|
||||
</ElFormItem>
|
||||
<ElFormItem label="描述" prop="description">
|
||||
<ElInput
|
||||
v-model="formData.description"
|
||||
:rows="4"
|
||||
:maxlength="100"
|
||||
show-word-limit
|
||||
type="textarea"
|
||||
placeholder="请输入描述"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElFormItem label="元数据" prop="i">
|
||||
<div class="flex flex-col gap-2">
|
||||
<div
|
||||
v-for="(item, index) in metadataList"
|
||||
:key="index"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<ElInput v-model="item.key" placeholder="键" />
|
||||
<ElInput v-model="item.value" placeholder="值" />
|
||||
<ElButton
|
||||
type="primary"
|
||||
icon="Plus"
|
||||
circle
|
||||
@click="metadataList.push({ key: '', value: '' })"
|
||||
/>
|
||||
<ElButton
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
circle
|
||||
@click="metadataList.splice(index, 1)"
|
||||
/>
|
||||
</div>
|
||||
<FaForm
|
||||
:key="demoFormRenderKey"
|
||||
scrollbar
|
||||
max-height="70vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="demoDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #i>
|
||||
<div class="flex flex-col gap-2">
|
||||
<div
|
||||
v-for="(item, index) in metadataList"
|
||||
:key="index"
|
||||
class="flex items-center gap-2"
|
||||
>
|
||||
<ElInput v-model="item.key" placeholder="键" />
|
||||
<ElInput v-model="item.value" placeholder="值" />
|
||||
<ElButton
|
||||
v-if="metadataList.length === 0"
|
||||
type="primary"
|
||||
icon="Plus"
|
||||
circle
|
||||
@click="metadataList.push({ key: '', value: '' })"
|
||||
>
|
||||
添加元数据
|
||||
</ElButton>
|
||||
/>
|
||||
<ElButton
|
||||
type="danger"
|
||||
icon="Delete"
|
||||
circle
|
||||
@click="metadataList.splice(index, 1)"
|
||||
/>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<ElButton
|
||||
v-if="metadataList.length === 0"
|
||||
type="primary"
|
||||
icon="Plus"
|
||||
@click="metadataList.push({ key: '', value: '' })"
|
||||
>
|
||||
添加元数据
|
||||
</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
|
||||
<FaImportDialog
|
||||
v-model="importModalVisible"
|
||||
v-model="importVisible"
|
||||
:content-config="demoImportContentConfig"
|
||||
default-template-file-name="demo_import_template.xlsx"
|
||||
@upload="handleCrudImportUpload"
|
||||
/>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="demoExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -272,25 +153,19 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref } from "vue";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaImportDialog from "@/components/modal/fa-import-dialog/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { IContentConfig, IObject } from "@/components/modal/types";
|
||||
import FaSearchBarWithAudit from "@/components/forms/fa-search-bar/FaSearchBarWithAudit.vue";
|
||||
import type { AuditSearchFormParams } from "@/components/forms/fa-search-bar/auditSearchFormItems";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import JsonPretty from "@/components/others/fa-json-pretty/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import DemoAPI, {
|
||||
type DemoForm,
|
||||
type DemoPageQuery,
|
||||
type DemoTable,
|
||||
} from "@/api/module_example/demo";
|
||||
import { ElMessage, ElMessageBox, ElTag } from "element-plus";
|
||||
import { ResultEnum } from "@/enums/api/result.enum";
|
||||
|
||||
defineOptions({
|
||||
@@ -502,6 +377,110 @@ const dialogVisible = reactive({
|
||||
|
||||
const detailFormData = ref<DemoTable>({});
|
||||
|
||||
const demoDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "名称", prop: "name" },
|
||||
{ label: "UUID", prop: "uuid" },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "整数", prop: "a" },
|
||||
{ label: "大整数", prop: "b" },
|
||||
{ label: "浮点数", prop: "c" },
|
||||
{
|
||||
label: "布尔值",
|
||||
prop: "d",
|
||||
tag: {
|
||||
map: { true: { type: "success", text: "是" }, false: { type: "danger", text: "否" } },
|
||||
},
|
||||
},
|
||||
{ label: "日期", prop: "e" },
|
||||
{ label: "时间", prop: "f" },
|
||||
{ label: "日期时间", prop: "g" },
|
||||
{ label: "长文本", prop: "h" },
|
||||
{ label: "元数据", prop: "i", slot: "i" },
|
||||
{ label: "描述", prop: "description" },
|
||||
{ label: "创建人", prop: "created_by.name" },
|
||||
{ label: "更新人", prop: "updated_by.name" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
];
|
||||
|
||||
// 示例页新增/编辑表单 —— 元数据字段用具名插槽渲染
|
||||
const demoDialogFormItems: FormItem[] = [
|
||||
{
|
||||
key: "name",
|
||||
label: "名称",
|
||||
type: "input",
|
||||
props: { placeholder: "请输入名称", maxlength: 50 },
|
||||
},
|
||||
{
|
||||
key: "status",
|
||||
label: "状态",
|
||||
type: "radiogroup",
|
||||
props: {
|
||||
options: [
|
||||
{ label: "启用", value: "0" },
|
||||
{ label: "停用", value: "1" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{ key: "a", label: "整数", type: "number", props: { placeholder: "请输入整数" } },
|
||||
{ key: "b", label: "大整数", type: "number", props: { placeholder: "请输入大整数" } },
|
||||
{
|
||||
key: "c",
|
||||
label: "浮点数",
|
||||
type: "number",
|
||||
props: { placeholder: "请输入浮点数", step: 0.01, precision: 2 },
|
||||
},
|
||||
{ key: "d", label: "布尔值", type: "switch" },
|
||||
{
|
||||
key: "e",
|
||||
label: "日期",
|
||||
type: "date",
|
||||
props: { placeholder: "请选择日期", valueFormat: "YYYY-MM-DD", style: "width: 100%" },
|
||||
},
|
||||
{
|
||||
key: "f",
|
||||
label: "时间",
|
||||
type: "timepicker",
|
||||
props: { placeholder: "请选择时间", valueFormat: "HH:mm:ss", style: "width: 100%" },
|
||||
},
|
||||
{
|
||||
key: "g",
|
||||
label: "日期时间",
|
||||
type: "datetime",
|
||||
props: {
|
||||
placeholder: "请选择日期时间",
|
||||
valueFormat: "YYYY-MM-DD HH:mm:ss",
|
||||
style: "width: 100%",
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "h",
|
||||
label: "长文本",
|
||||
type: "input",
|
||||
props: { type: "textarea", rows: 4, placeholder: "请输入长文本" },
|
||||
},
|
||||
{
|
||||
key: "description",
|
||||
label: "描述",
|
||||
type: "input",
|
||||
props: {
|
||||
type: "textarea",
|
||||
rows: 4,
|
||||
maxlength: 100,
|
||||
showWordLimit: true,
|
||||
placeholder: "请输入描述",
|
||||
},
|
||||
},
|
||||
{ key: "i", label: "元数据", type: "input" /* 由 #i 插槽接管 */ },
|
||||
];
|
||||
|
||||
const formData = ref<DemoForm>({
|
||||
id: undefined,
|
||||
name: "",
|
||||
@@ -523,11 +502,12 @@ const rules = reactive({
|
||||
status: [{ required: true, message: "请选择状态", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const dataFormRef = ref();
|
||||
const dataFormRef = ref<InstanceType<typeof FaForm> | null>(null);
|
||||
const submitLoading = ref(false);
|
||||
const demoFormRenderKey = ref(0);
|
||||
const metadataList = ref<{ key: string; value: string }[]>([]);
|
||||
|
||||
const importModalVisible = ref(false);
|
||||
const exportModalVisible = ref(false);
|
||||
const { importVisible, exportVisible, openImport, openExport } = useImportExport();
|
||||
|
||||
const initialFormData: DemoForm = {
|
||||
id: undefined,
|
||||
@@ -628,6 +608,7 @@ async function openEditDialog(type: "add" | "edit", row?: DemoTable) {
|
||||
Object.assign(formData.value, initialFormData);
|
||||
formData.value.id = undefined;
|
||||
metadataList.value = [];
|
||||
demoFormRenderKey.value += 1;
|
||||
} else if (row?.id) {
|
||||
dialogVisible.title = "修改";
|
||||
const response = await DemoAPI.getDemoDetail(row.id);
|
||||
@@ -659,7 +640,7 @@ async function handleCloseDialog() {
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
dataFormRef.value.validate(async (valid: boolean) => {
|
||||
dataFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (!valid) return;
|
||||
const submitData = { ...formData.value };
|
||||
if (metadataList.value.length > 0) {
|
||||
@@ -757,10 +738,6 @@ async function runBatchStatus(status: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function openImportModal() {
|
||||
importModalVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleCrudImportUpload(formData: FormData) {
|
||||
try {
|
||||
const res = await DemoAPI.importDemo(formData);
|
||||
@@ -769,15 +746,11 @@ async function handleCrudImportUpload(formData: FormData) {
|
||||
return;
|
||||
}
|
||||
ElMessage.success(res.data.msg || "导入成功");
|
||||
importModalVisible.value = false;
|
||||
importVisible.value = false;
|
||||
await refreshData();
|
||||
} catch (error) {
|
||||
console.error("[Import]", error);
|
||||
ElMessage.error("导入失败");
|
||||
}
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -38,8 +38,8 @@
|
||||
:perm-patch="['module_example:demo01:patch']"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="openEditDialog('add')"
|
||||
@import="openImportModal"
|
||||
@export="openExportModal"
|
||||
@import="openImport"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
@more="runBatchStatus"
|
||||
/>
|
||||
@@ -64,87 +64,56 @@
|
||||
width="768px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="名称" :span="2">
|
||||
{{ detailFormData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="UUID" :span="2">
|
||||
{{ detailFormData.uuid }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag :type="detailFormData.status === '0' ? 'success' : 'danger'">
|
||||
{{ detailFormData.status === "0" ? "正常" : "停用" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="2">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建人" :span="2">
|
||||
{{ detailFormData.created_by?.name ?? "—" }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新人" :span="2">
|
||||
{{ detailFormData.updated_by?.name ?? "—" }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="2"
|
||||
:data="detailFormData"
|
||||
:items="demo01DetailItems"
|
||||
max-height="70vh"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<FaForm
|
||||
:key="demo01FormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="demo01DialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">正常</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<FaForm
|
||||
:key="demo01FormRenderKey"
|
||||
scrollbar
|
||||
max-height="70vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="demo01DialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">正常</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
|
||||
<FaImportDialog
|
||||
v-model="importModalVisible"
|
||||
v-model="importVisible"
|
||||
:content-config="demo01ImportContentConfig"
|
||||
default-template-file-name="demo01_import_template.xlsx"
|
||||
@upload="handleCrudImportUpload"
|
||||
/>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="demo01ExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -154,18 +123,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, reactive } from "vue";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaImportDialog from "@/components/modal/fa-import-dialog/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { IContentConfig, IObject } from "@/components/modal/types";
|
||||
import FaSearchBarWithAudit from "@/components/forms/fa-search-bar/FaSearchBarWithAudit.vue";
|
||||
import type { AuditSearchFormParams } from "@/components/forms/fa-search-bar/auditSearchFormItems";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import Demo01API, {
|
||||
@@ -373,6 +336,24 @@ const dialogVisible = reactive({
|
||||
|
||||
const detailFormData = ref<Demo01Table>({});
|
||||
|
||||
const demo01DetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "名称", prop: "name" },
|
||||
{ label: "UUID", prop: "uuid" },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "正常" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "描述", prop: "description" },
|
||||
{ label: "创建人", prop: "created_by.name" },
|
||||
{ label: "更新人", prop: "updated_by.name" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
];
|
||||
|
||||
const formData = ref<Demo01Form>({
|
||||
id: undefined,
|
||||
name: "",
|
||||
@@ -386,6 +367,7 @@ const rules = reactive({
|
||||
});
|
||||
|
||||
const dataFormRef = ref<InstanceType<typeof FaForm> | null>(null);
|
||||
const submitLoading = ref(false);
|
||||
const demo01FormRenderKey = ref(0);
|
||||
|
||||
const demo01DialogFormItems = computed<FormItem[]>(() => [
|
||||
@@ -412,8 +394,7 @@ const demo01DialogFormItems = computed<FormItem[]>(() => [
|
||||
},
|
||||
]);
|
||||
|
||||
const importModalVisible = ref(false);
|
||||
const exportModalVisible = ref(false);
|
||||
const { importVisible, exportVisible, openImport, openExport } = useImportExport();
|
||||
|
||||
const initialFormData: Demo01Form = {
|
||||
id: undefined,
|
||||
@@ -516,8 +497,8 @@ async function openEditDialog(type: "add" | "edit", row?: Demo01Table) {
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData.value, initialFormData);
|
||||
}
|
||||
|
||||
@@ -605,10 +586,6 @@ async function runBatchStatus(status: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function openImportModal() {
|
||||
importModalVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleCrudImportUpload(formDataUpload: FormData) {
|
||||
try {
|
||||
const res = await Demo01API.importDemo01(formDataUpload);
|
||||
@@ -617,17 +594,13 @@ async function handleCrudImportUpload(formDataUpload: FormData) {
|
||||
return;
|
||||
}
|
||||
ElMessage.success(res.data.msg || "导入成功");
|
||||
importModalVisible.value = false;
|
||||
importVisible.value = false;
|
||||
await refreshData();
|
||||
} catch (error) {
|
||||
console.error("[Import]", error);
|
||||
ElMessage.error("导入失败");
|
||||
}
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
<div v-show="editMode === 'visual'" class="visual-pane">
|
||||
<ElScrollbar max-height="min(58vh, 520px)" class="visual-pane-scroll">
|
||||
<div class="visual-structure max-w-3xl">
|
||||
<ElDescriptions :column="1" border size="small" class="visual-desc">
|
||||
<FaDescriptions :column="1" size="small" class="visual-desc" :scrollbar="false">
|
||||
<template #title>
|
||||
<span class="text-sm font-medium text-[var(--el-text-color-primary)]">选项</span>
|
||||
</template>
|
||||
@@ -81,9 +81,9 @@
|
||||
<ElRadioButton value="masterSub">主表 + 子表明细</ElRadioButton>
|
||||
</ElRadioGroup>
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</FaDescriptions>
|
||||
|
||||
<ElDescriptions :column="1" border size="small" class="visual-desc mt-3">
|
||||
<FaDescriptions :column="1" size="small" class="visual-desc mt-3" :scrollbar="false">
|
||||
<template #title>
|
||||
<span class="text-sm font-medium text-[var(--el-text-color-primary)]">主表</span>
|
||||
</template>
|
||||
@@ -101,14 +101,14 @@
|
||||
<ElDescriptionsItem label="说明" label-class-name="visual-desc-label">
|
||||
<ElInput v-model="visual.mainComment" placeholder="中文说明,可选" clearable />
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</FaDescriptions>
|
||||
|
||||
<ElDescriptions
|
||||
<FaDescriptions
|
||||
v-if="visual.subEnabled"
|
||||
:column="1"
|
||||
border
|
||||
size="small"
|
||||
class="visual-desc mt-3"
|
||||
:scrollbar="false"
|
||||
>
|
||||
<template #title>
|
||||
<span class="text-sm font-medium text-[var(--el-text-color-primary)]">子表</span>
|
||||
@@ -137,7 +137,7 @@
|
||||
<ElDescriptionsItem label="对应主表列" label-class-name="visual-desc-label">
|
||||
<ElInput v-model="visual.fkRefColumn" placeholder="一般是 id" clearable />
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</FaDescriptions>
|
||||
|
||||
<div class="mt-3">
|
||||
<ElLink type="primary" underline="never" @click="syncVisualToSql">
|
||||
@@ -179,6 +179,7 @@ import { ElMessage } from "element-plus";
|
||||
import { ArrowDown, CopyDocument } from "@element-plus/icons-vue";
|
||||
import { useClipboard } from "@vueuse/core";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaDescriptions from "@/components/others/fa-descriptions/index.vue";
|
||||
import { useSettingsStore } from "@stores";
|
||||
import { ThemeMode } from "@/enums/settings/theme.enum";
|
||||
import { buildSqlFromVisual } from "../utils/buildCreateTableSql";
|
||||
|
||||
+3
-2
@@ -13,7 +13,7 @@
|
||||
<span class="font-medium">Redis监控信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<ElDescriptions :column="6" border>
|
||||
<FaDescriptions :column="6" :scrollbar="false">
|
||||
<ElDescriptionsItem label="Redis版本">
|
||||
{{ cache.info?.redis_version || "-" }}
|
||||
</ElDescriptionsItem>
|
||||
@@ -55,7 +55,7 @@
|
||||
{{ cache.info?.instantaneous_input_kbps || 0 }}kps/
|
||||
{{ cache.info?.instantaneous_output_kbps || 0 }}kps
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</FaDescriptions>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
@@ -262,6 +262,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import FaDescriptions from "@/components/others/fa-descriptions/index.vue";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import CacheAPI, {
|
||||
type CacheInfo,
|
||||
|
||||
@@ -66,7 +66,6 @@ import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import ArtButtonTable from "@/components/forms/fa-button-table/index.vue";
|
||||
import CopyButton from "@/components/others/fa-copy-button/index.vue";
|
||||
import OnlineAPI, { type OnlineUserTable } from "@/api/module_monitor/online";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
@@ -235,7 +234,7 @@ const {
|
||||
}
|
||||
return h(ElTooltip, { content: "强退", placement: "top" }, () =>
|
||||
h("span", { class: "inline-flex" }, [
|
||||
h(ArtButtonTable, {
|
||||
h(FaButtonTable, {
|
||||
type: "delete",
|
||||
onClick: () => kickSession(row.session_id),
|
||||
}),
|
||||
|
||||
@@ -222,7 +222,6 @@ import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import ArtButtonTable from "@/components/forms/fa-button-table/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import { ResourceAPI, type ResourceItem } from "@/api/module_monitor/resource";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
@@ -406,7 +405,7 @@ const {
|
||||
!row.is_dir && hasAuth("module_monitor:resource:download")
|
||||
? h(ElTooltip, { content: "下载", placement: "top" }, () =>
|
||||
h("span", { class: "inline-flex" }, [
|
||||
h(ArtButtonTable, {
|
||||
h(FaButtonTable, {
|
||||
type: "view",
|
||||
icon: "ri:download-line",
|
||||
onClick: () => void handleDownload(row),
|
||||
@@ -417,7 +416,7 @@ const {
|
||||
hasAuth("module_monitor:resource:rename")
|
||||
? h(ElTooltip, { content: "重命名", placement: "top" }, () =>
|
||||
h("span", { class: "inline-flex" }, [
|
||||
h(ArtButtonTable, {
|
||||
h(FaButtonTable, {
|
||||
type: "edit",
|
||||
onClick: () => void handleRenameOpen(row),
|
||||
}),
|
||||
@@ -427,7 +426,7 @@ const {
|
||||
hasAuth("module_monitor:resource:delete")
|
||||
? h(ElTooltip, { content: "删除", placement: "top" }, () =>
|
||||
h("span", { class: "inline-flex" }, [
|
||||
h(ArtButtonTable, {
|
||||
h(FaButtonTable, {
|
||||
type: "delete",
|
||||
onClick: () => void handleDelete(row),
|
||||
}),
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
>
|
||||
<span class="text-sm">{{ (server.cpu?.used || 0).toFixed(1) }}%</span>
|
||||
</ElProgress>
|
||||
<ElDescriptions :column="2" border size="small">
|
||||
<FaDescriptions :column="2" size="small" :scrollbar="false">
|
||||
<ElDescriptionsItem label="核心数">
|
||||
{{ server.cpu?.cpu_num || 0 }}
|
||||
</ElDescriptionsItem>
|
||||
@@ -38,7 +38,7 @@
|
||||
<ElDescriptionsItem label="系统使用率">
|
||||
{{ (server.cpu?.sys || 0).toFixed(1) }}%
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</FaDescriptions>
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
@@ -68,7 +68,7 @@
|
||||
>
|
||||
<span class="text-sm">{{ (server.mem?.usage || 0).toFixed(1) }}%</span>
|
||||
</ElProgress>
|
||||
<ElDescriptions :column="2" border size="small">
|
||||
<FaDescriptions :column="2" size="small" :scrollbar="false">
|
||||
<ElDescriptionsItem label="总内存">
|
||||
{{ server.mem?.total }}
|
||||
</ElDescriptionsItem>
|
||||
@@ -81,7 +81,7 @@
|
||||
<ElDescriptionsItem label="Python内存">
|
||||
{{ server.py?.memory_usage ? server.py.memory_usage.toFixed(1) + "%" : "-" }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</FaDescriptions>
|
||||
</div>
|
||||
</div>
|
||||
</ElCard>
|
||||
@@ -98,7 +98,7 @@
|
||||
<span class="font-medium">服务器基本信息</span>
|
||||
</div>
|
||||
</template>
|
||||
<ElDescriptions :column="2" border size="small">
|
||||
<FaDescriptions :column="2" size="small" :scrollbar="false">
|
||||
<ElDescriptionsItem label="服务器名称">
|
||||
{{ server.sys?.computer_name || "-" }}
|
||||
</ElDescriptionsItem>
|
||||
@@ -111,7 +111,7 @@
|
||||
<ElDescriptionsItem label="系统架构">
|
||||
{{ server.sys?.os_arch || "-" }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</FaDescriptions>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
<span class="font-medium">Python运行环境</span>
|
||||
</div>
|
||||
</template>
|
||||
<ElDescriptions :column="2" border size="small">
|
||||
<FaDescriptions :column="2" size="small" :scrollbar="false">
|
||||
<ElDescriptionsItem label="Python名称">
|
||||
{{ server.py?.name || "-" }}
|
||||
</ElDescriptionsItem>
|
||||
@@ -143,7 +143,7 @@
|
||||
<ElDescriptionsItem label="项目路径" :span="2">
|
||||
{{ server.sys?.user_dir || "-" }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</FaDescriptions>
|
||||
</ElCard>
|
||||
</ElCol>
|
||||
</ElRow>
|
||||
@@ -186,6 +186,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import FaDescriptions from "@/components/others/fa-descriptions/index.vue";
|
||||
import ServerAPI, { type ServerInfo } from "@/api/module_monitor/server";
|
||||
|
||||
defineOptions({ name: "ServerMonitor" });
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- 部门配置:Art + 树形表格(对齐 system/menu 模版) -->
|
||||
<!-- 部门配置:FA + 树形表格(对齐 system/menu 模版) -->
|
||||
<template>
|
||||
<div class="fa-full-height">
|
||||
<FaSearchBar
|
||||
@@ -63,97 +63,58 @@
|
||||
width="640px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptionsItem label="部门名称" :span="2">
|
||||
{{ detailFormData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="部门编码" :span="2">
|
||||
{{ detailFormData.code }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="上级部门" :span="2">
|
||||
{{ detailFormData.parent_name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag :type="detailFormData.status === '0' ? 'success' : 'danger'">
|
||||
{{ detailFormData.status === "0" ? "启用" : "停用" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="排序" :span="2">
|
||||
{{ detailFormData.order }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="4">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="4"
|
||||
:data="detailFormData"
|
||||
:items="deptDetailItems"
|
||||
max-height="75vh"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<FaForm
|
||||
:key="deptFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="deptDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<FaForm
|
||||
:key="deptFormRenderKey"
|
||||
scrollbar
|
||||
max-height="75vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="deptDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, ref, reactive, computed, nextTick, onMounted } from "vue";
|
||||
import { useTableColumns } from "@/hooks/core/useTableColumns";
|
||||
import DeptAPI, {
|
||||
type DeptForm,
|
||||
type DeptPageQuery,
|
||||
type DeptTable,
|
||||
} from "@/api/module_system/dept";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import { ElMessage, ElMessageBox, ElTag } from "element-plus";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { useUserStore } from "@stores";
|
||||
import { formatTree } from "@utils/common";
|
||||
@@ -368,6 +329,24 @@ const { columnChecks, columns } = useTableColumns<DeptTable>(() => [
|
||||
|
||||
const detailFormData = ref<DeptTable>({ code: "" });
|
||||
|
||||
const deptDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "部门名称", prop: "name" },
|
||||
{ label: "部门编码", prop: "code" },
|
||||
{ label: "上级部门", prop: "parent_name" },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "排序", prop: "order" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
{ label: "描述", prop: "description", span: 4 },
|
||||
];
|
||||
|
||||
const formData = ref<DeptForm>({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
@@ -411,6 +390,7 @@ const initialFormData: DeptForm = {
|
||||
};
|
||||
|
||||
const dataFormRef = ref<InstanceType<typeof FaForm> | null>(null);
|
||||
const submitLoading = ref(false);
|
||||
const deptFormRenderKey = ref(0);
|
||||
|
||||
const deptDialogFormItems = computed<FormItem[]>(() => [
|
||||
@@ -494,8 +474,8 @@ function onResetSearch() {
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, initialFormData);
|
||||
}
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
:perm-patch="['module_system:dict_data:patch']"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="handleOpenDialog('create')"
|
||||
@export="openExportModal"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
@more="handleMoreClick"
|
||||
/>
|
||||
@@ -67,167 +67,125 @@
|
||||
width="720px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="数据标签" :span="2">
|
||||
{{ detailFormData.dict_label }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="数据类型" :span="2">
|
||||
{{ detailFormData.dict_type }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="数据值" :span="2">
|
||||
{{ detailFormData.dict_value }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="样式属性" :span="2">
|
||||
{{ detailFormData.css_class }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="列表样式属性" :span="2">
|
||||
{{ detailFormData.list_class }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="是否默认" :span="2">
|
||||
<ElTag v-if="detailFormData.is_default" type="success">是</ElTag>
|
||||
<ElTag v-else type="danger">否</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag v-if="detailFormData.status === '0'" type="success">启用</ElTag>
|
||||
<ElTag v-else type="danger">停用</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="排序" :span="2">
|
||||
{{ detailFormData.dict_sort }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="2">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="2"
|
||||
:data="detailFormData"
|
||||
:items="dictDataDetailItems"
|
||||
max-height="70vh"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<FaForm
|
||||
:key="dictDataFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="dictDataDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #css_class>
|
||||
<ElSelect
|
||||
v-model="formData.css_class"
|
||||
placeholder="请选择常用颜色或输入自定义"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<ElOption value="primary" label="主要(primary)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('primary')">
|
||||
主要(primary)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="success" label="成功(success)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('success')">
|
||||
成功(success)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="warning" label="警告(warning)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('warning')">
|
||||
警告(warning)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="danger" label="危险(danger)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('danger')">
|
||||
危险(danger)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="info" label="信息(info)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('info')">
|
||||
信息(info)
|
||||
</span>
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</template>
|
||||
<template #list_class>
|
||||
<ElSelect v-model="formData.list_class" placeholder="请选择列表类样式" clearable>
|
||||
<ElOption value="default" label="默认(default)">
|
||||
<span class="tag-option-preview tag-option-preview--default">
|
||||
默认(default)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="primary" label="主要(primary)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('primary')">
|
||||
主要(primary)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="success" label="成功(success)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('success')">
|
||||
成功(success)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="warning" label="警告(warning)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('warning')">
|
||||
警告(warning)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="danger" label="危险(danger)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('danger')">
|
||||
危险(danger)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="info" label="信息(info)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('info')">
|
||||
信息(info)
|
||||
</span>
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</template>
|
||||
<template #is_default>
|
||||
<ElRadioGroup v-model="formData.is_default">
|
||||
<ElRadio :value="true">是</ElRadio>
|
||||
<ElRadio :value="false">否</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
<template #status>
|
||||
<ElSwitch
|
||||
v-model="formData.status"
|
||||
inline-prompt
|
||||
:active-value="'0'"
|
||||
:inactive-value="'1'"
|
||||
/>
|
||||
</template>
|
||||
</FaForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<FaForm
|
||||
:key="dictDataFormRenderKey"
|
||||
scrollbar
|
||||
max-height="70vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="dictDataDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #css_class>
|
||||
<ElSelect
|
||||
v-model="formData.css_class"
|
||||
placeholder="请选择常用颜色或输入自定义"
|
||||
clearable
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
>
|
||||
<ElOption value="primary" label="主要(primary)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('primary')">
|
||||
主要(primary)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="success" label="成功(success)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('success')">
|
||||
成功(success)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="warning" label="警告(warning)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('warning')">
|
||||
警告(warning)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="danger" label="危险(danger)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('danger')">
|
||||
危险(danger)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="info" label="信息(info)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('info')">
|
||||
信息(info)
|
||||
</span>
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</template>
|
||||
<template #list_class>
|
||||
<ElSelect v-model="formData.list_class" placeholder="请选择列表类样式" clearable>
|
||||
<ElOption value="default" label="默认(default)">
|
||||
<span class="tag-option-preview tag-option-preview--default">默认(default)</span>
|
||||
</ElOption>
|
||||
<ElOption value="primary" label="主要(primary)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('primary')">
|
||||
主要(primary)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="success" label="成功(success)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('success')">
|
||||
成功(success)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="warning" label="警告(warning)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('warning')">
|
||||
警告(warning)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="danger" label="危险(danger)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('danger')">
|
||||
危险(danger)
|
||||
</span>
|
||||
</ElOption>
|
||||
<ElOption value="info" label="信息(info)">
|
||||
<span class="tag-option-preview" :style="getTagPreviewStyle('info')">
|
||||
信息(info)
|
||||
</span>
|
||||
</ElOption>
|
||||
</ElSelect>
|
||||
</template>
|
||||
<template #is_default>
|
||||
<ElRadioGroup v-model="formData.is_default">
|
||||
<ElRadio :value="true">是</ElRadio>
|
||||
<ElRadio :value="false">否</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
<template #status>
|
||||
<ElSwitch
|
||||
v-model="formData.status"
|
||||
inline-prompt
|
||||
:active-value="'0'"
|
||||
:inactive-value="'1'"
|
||||
/>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="dictDataExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -238,16 +196,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, reactive } from "vue";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { IObject } from "@/components/modal/types";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaDrawer from "@/components/modal/fa-drawer/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import DictAPI, {
|
||||
@@ -255,7 +207,6 @@ import DictAPI, {
|
||||
type DictDataPageQuery,
|
||||
type DictDataTable,
|
||||
} from "@/api/module_system/dict";
|
||||
import { ElMessage, ElMessageBox, ElTag } from "element-plus";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
import { useDictStore } from "@stores";
|
||||
@@ -542,6 +493,33 @@ const dialogVisible = reactive({
|
||||
|
||||
const detailFormData = ref<DictDataTable>({});
|
||||
|
||||
const dictDataDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "数据标签", prop: "dict_label" },
|
||||
{ label: "数据类型", prop: "dict_type" },
|
||||
{ label: "数据值", prop: "dict_value" },
|
||||
{ label: "样式属性", prop: "css_class" },
|
||||
{ label: "列表样式属性", prop: "list_class" },
|
||||
{
|
||||
label: "是否默认",
|
||||
prop: "is_default",
|
||||
tag: {
|
||||
map: { true: { type: "success", text: "是" }, false: { type: "danger", text: "否" } },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "排序", prop: "dict_sort" },
|
||||
{ label: "描述", prop: "description" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
];
|
||||
|
||||
const formData = ref<DictDataForm>({
|
||||
id: undefined,
|
||||
dict_sort: 1,
|
||||
@@ -566,6 +544,7 @@ const rules = reactive({
|
||||
});
|
||||
|
||||
const dataFormRef = ref<InstanceType<typeof FaForm> | null>(null);
|
||||
const submitLoading = ref(false);
|
||||
const dictDataFormRenderKey = ref(0);
|
||||
|
||||
const dictDataDialogFormItems = computed<FormItem[]>(() => [
|
||||
@@ -662,7 +641,7 @@ const initialFormData: DictDataForm = {
|
||||
dict_type_id: props.dictTypeId,
|
||||
};
|
||||
|
||||
const exportModalVisible = ref(false);
|
||||
const { exportVisible, openExport } = useImportExport();
|
||||
|
||||
async function handleSearchBarSearch(params: DictDataSearchForm) {
|
||||
await searchBarRef.value?.validate?.();
|
||||
@@ -689,8 +668,8 @@ function onResetSearch() {
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, {
|
||||
...initialFormData,
|
||||
dict_type_id: props.dictTypeId,
|
||||
@@ -848,10 +827,6 @@ async function handleMoreClick(status: string) {
|
||||
// 用户取消
|
||||
}
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
:perm-patch="['module_system:dict_type:patch']"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="handleOpenDialog('create')"
|
||||
@export="openExportModal"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
@more="handleMoreClick"
|
||||
/>
|
||||
@@ -62,73 +62,53 @@
|
||||
width="640px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="字典名称" :span="2">
|
||||
{{ detailFormData.dict_name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="字典类型" :span="2">
|
||||
<ElTag type="primary">{{ detailFormData.dict_type }}</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag v-if="detailFormData.status === '0'" type="success">启用</ElTag>
|
||||
<ElTag v-else type="danger">停用</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="2">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="2"
|
||||
:data="detailFormData"
|
||||
:items="dictDetailItems"
|
||||
max-height="70vh"
|
||||
>
|
||||
<template #dict_type="{ row }">
|
||||
<ElTag type="primary">{{ row?.dict_type }}</ElTag>
|
||||
</template>
|
||||
</FaDescriptions>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="70vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<FaForm
|
||||
:key="dictFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="dictDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton v-if="dialogVisible.type !== 'detail'" type="primary" @click="handleSubmit">
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<FaForm
|
||||
:key="dictFormRenderKey"
|
||||
scrollbar
|
||||
max-height="70vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="dictDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="dictTypeExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -146,15 +126,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, reactive } from "vue";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { IObject } from "@/components/modal/types";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import DictAPI, {
|
||||
@@ -162,7 +137,6 @@ import DictAPI, {
|
||||
type DictPageQuery,
|
||||
type DictTable,
|
||||
} from "@/api/module_system/dict";
|
||||
import { ElMessage, ElMessageBox, ElTag } from "element-plus";
|
||||
import { useDictStore } from "@stores";
|
||||
import DataDrawer from "@views/module_system/dict/components/DataDrawer.vue";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
@@ -364,6 +338,22 @@ const dialogVisible = reactive({
|
||||
|
||||
const detailFormData = ref<DictTable>({});
|
||||
|
||||
const dictDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "字典名称", prop: "dict_name" },
|
||||
{ label: "字典类型", prop: "dict_type", slot: "dict_type" },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "描述", prop: "description" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
];
|
||||
|
||||
const formData = ref<DictForm>({
|
||||
id: undefined,
|
||||
dict_name: "",
|
||||
@@ -379,6 +369,7 @@ const rules = reactive({
|
||||
});
|
||||
|
||||
const dataFormRef = ref<InstanceType<typeof FaForm> | null>(null);
|
||||
const submitLoading = ref(false);
|
||||
const dictFormRenderKey = ref(0);
|
||||
|
||||
const dictDialogFormItems = computed<FormItem[]>(() => [
|
||||
@@ -426,7 +417,7 @@ const initialFormData: DictForm = {
|
||||
description: undefined,
|
||||
};
|
||||
|
||||
const exportModalVisible = ref(false);
|
||||
const { exportVisible, openExport } = useImportExport();
|
||||
|
||||
const drawerVisible = ref(false);
|
||||
const currentDictType = ref("");
|
||||
@@ -511,8 +502,8 @@ function handleDictDataDrawer(dictTypeRow: DictTable) {
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, initialFormData);
|
||||
}
|
||||
|
||||
@@ -629,10 +620,6 @@ async function handleMoreClick(status: string) {
|
||||
// 用户取消
|
||||
}
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -43,7 +43,7 @@
|
||||
:perm-export="['module_system:log:export']"
|
||||
:perm-delete="['module_system:log:delete']"
|
||||
:delete-loading="batchDeleting"
|
||||
@export="openExportModal"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
/>
|
||||
</template>
|
||||
@@ -67,77 +67,42 @@
|
||||
width="960px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
form-mode="detail"
|
||||
@confirm="handleCloseDialog"
|
||||
>
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="8" border label-width="200px">
|
||||
<ElDescriptionsItem label="日志类型" :span="2">
|
||||
<ElTag :type="formData.type === 1 ? 'success' : 'primary'">
|
||||
{{ formData.type === 1 ? "登录日志" : "操作日志" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="请求路径" :span="2">
|
||||
{{ formData.request_path }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="请求方法" :span="2">
|
||||
<ElTag :type="getMethodType(formData.request_method)">
|
||||
{{ formData.request_method }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="响应状态码" :span="2">
|
||||
<ElTag :type="getStatusCodeType(formData.response_code)">
|
||||
{{ formData.response_code }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="请求IP" :span="2">
|
||||
{{ formData.request_ip }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="处理时间" :span="2">
|
||||
{{ formData.process_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="浏览器" :span="2">
|
||||
{{ formData.request_browser }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="操作系统" :span="2">
|
||||
{{ formData.request_os }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="请求参数" :span="4">
|
||||
<JsonPretty :value="formData.request_payload" height="80px" />
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="响应数据" :span="4">
|
||||
<JsonPretty :value="formData.response_json" height="140px" />
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="登录地点" :span="2">
|
||||
{{ formData.login_location }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="4">
|
||||
{{ formData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建人" :span="2">
|
||||
{{ formData.created_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新人" :span="2">
|
||||
{{ formData.updated_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ formData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ formData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
<FaDescriptions
|
||||
:column="8"
|
||||
:data="formData"
|
||||
:items="logDetailItems"
|
||||
label-width="200px"
|
||||
max-height="75vh"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<ElTag :type="row?.type === 1 ? 'success' : 'primary'">
|
||||
{{ row?.type === 1 ? "登录日志" : "操作日志" }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<template #request_method="{ row }">
|
||||
<ElTag :type="getMethodType(row?.request_method as string)">
|
||||
{{ row?.request_method }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<template #response_code="{ row }">
|
||||
<ElTag :type="getStatusCodeType(row?.response_code as number)">
|
||||
{{ row?.response_code }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<template #request_payload="{ row }">
|
||||
<JsonPretty :value="row?.request_payload as any" height="80px" />
|
||||
</template>
|
||||
<template #response_json="{ row }">
|
||||
<JsonPretty :value="row?.response_json as any" height="140px" />
|
||||
</template>
|
||||
</FaDescriptions>
|
||||
</FaDialog>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="logExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -147,21 +112,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, nextTick } from "vue";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { IObject } from "@/components/modal/types";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import JsonPretty from "@/components/others/fa-json-pretty/index.vue";
|
||||
import CopyButton from "@/components/others/fa-copy-button/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import LogAPI, { type LogPageQuery, type LogTable } from "@/api/module_system/log";
|
||||
import { ElMessage, ElMessageBox, ElTag } from "element-plus";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
import UserTableSelect from "@views/module_system/user/components/UserTableSelect.vue";
|
||||
@@ -400,6 +356,25 @@ const logExportContentConfig = computed(() => ({
|
||||
|
||||
const formData = ref<LogTable>({});
|
||||
|
||||
const logDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] = [
|
||||
{ label: "日志类型", prop: "type", slot: "type" },
|
||||
{ label: "请求路径", prop: "request_path" },
|
||||
{ label: "请求方法", prop: "request_method", slot: "request_method" },
|
||||
{ label: "响应状态码", prop: "response_code", slot: "response_code" },
|
||||
{ label: "请求IP", prop: "request_ip" },
|
||||
{ label: "处理时间", prop: "process_time" },
|
||||
{ label: "浏览器", prop: "request_browser" },
|
||||
{ label: "操作系统", prop: "request_os" },
|
||||
{ label: "请求参数", prop: "request_payload", slot: "request_payload", span: 4 },
|
||||
{ label: "响应数据", prop: "response_json", slot: "response_json", span: 4 },
|
||||
{ label: "登录地点", prop: "login_location" },
|
||||
{ label: "描述", prop: "description", span: 4 },
|
||||
{ label: "创建人", prop: "created_by.name" },
|
||||
{ label: "更新人", prop: "updated_by.name" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
];
|
||||
|
||||
const dialogVisible = ref({
|
||||
title: "",
|
||||
visible: false,
|
||||
@@ -421,7 +396,7 @@ function getMethodType(method?: string) {
|
||||
return "info";
|
||||
}
|
||||
|
||||
const exportModalVisible = ref(false);
|
||||
const { exportVisible, openExport } = useImportExport();
|
||||
|
||||
async function handleSearchBarSearch(params: LogSearchForm) {
|
||||
await searchBarRef.value?.validate?.();
|
||||
@@ -533,8 +508,4 @@ async function handleBatchDelete() {
|
||||
batchDeleting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -71,122 +71,57 @@
|
||||
v-model="dialogVisible.visible"
|
||||
:title="dialogVisible.title"
|
||||
:size="drawerSize"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<!-- 详情 -->
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptionsItem label="编号" :span="2">
|
||||
{{ detailFormData.id }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="菜单名称" :span="2">
|
||||
{{ detailFormData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="菜单类型" :span="2">
|
||||
<ElTag v-if="detailFormData.type === MenuTypeEnum.CATALOG" type="warning">目录</ElTag>
|
||||
<ElTag v-if="detailFormData.type === MenuTypeEnum.MENU" type="success">菜单</ElTag>
|
||||
<ElTag v-if="detailFormData.type === MenuTypeEnum.BUTTON" type="danger">按钮</ElTag>
|
||||
<ElTag v-if="detailFormData.type === MenuTypeEnum.EXTLINK" type="info">外链</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="终端" :span="2">
|
||||
<ElTag v-if="detailFormData.client === MenuClientEnum.PC" type="primary">PC</ElTag>
|
||||
<ElTag v-else-if="detailFormData.client === MenuClientEnum.APP" type="success">
|
||||
APP
|
||||
</ElTag>
|
||||
<ElTag v-else type="info">{{ detailFormData.client || "—" }}</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="图标" :span="2">
|
||||
<template #default>
|
||||
<template v-if="detailFormData.icon">
|
||||
<MenuRouteIcon :icon="detailFormData.icon" :style="'vertical-align: -0.15em'" />
|
||||
</template>
|
||||
<FaDescriptions
|
||||
:column="4"
|
||||
:data="detailFormData"
|
||||
:items="menuDetailItems"
|
||||
:scrollbar="false"
|
||||
>
|
||||
<template #type="{ row }">
|
||||
<ElTag v-if="row?.type === MenuTypeEnum.CATALOG" type="warning">目录</ElTag>
|
||||
<ElTag v-if="row?.type === MenuTypeEnum.MENU" type="success">菜单</ElTag>
|
||||
<ElTag v-if="row?.type === MenuTypeEnum.BUTTON" type="danger">按钮</ElTag>
|
||||
<ElTag v-if="row?.type === MenuTypeEnum.EXTLINK" type="info">外链</ElTag>
|
||||
</template>
|
||||
<template #client="{ row }">
|
||||
<ElTag v-if="row?.client === MenuClientEnum.PC" type="primary">PC</ElTag>
|
||||
<ElTag v-else-if="row?.client === MenuClientEnum.APP" type="success">APP</ElTag>
|
||||
<ElTag v-else type="info">{{ row?.client || "—" }}</ElTag>
|
||||
</template>
|
||||
<template #icon="{ row }">
|
||||
<template v-if="row?.icon">
|
||||
<MenuRouteIcon :icon="row?.icon as string" :style="'vertical-align: -0.15em'" />
|
||||
</template>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="排序" :span="2">
|
||||
{{ detailFormData.order }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="权限标识" :span="2">
|
||||
{{ detailFormData.permission }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="路由名称" :span="2">
|
||||
{{ detailFormData.route_name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="路由路径" :span="2">
|
||||
{{ detailFormData.route_path }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="组件路径" :span="2">
|
||||
{{ detailFormData.component_path }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="重定向" :span="2">
|
||||
{{ detailFormData.redirect }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="父级编号" :span="2">
|
||||
{{ detailFormData.parent_id }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="父级菜单" :span="2">
|
||||
{{ detailFormData.parent_name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="是否缓存" :span="2">
|
||||
<ElTag :type="detailFormData.keep_alive ? 'success' : 'danger'">
|
||||
{{ detailFormData.keep_alive ? "是" : "否" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="是否显示" :span="2">
|
||||
<ElTag :type="detailFormData.hidden ? 'success' : 'danger'">
|
||||
{{ detailFormData.hidden ? "是" : "否" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="是否显示根路由" :span="2">
|
||||
<ElTag :type="detailFormData.always_show ? 'success' : 'danger'">
|
||||
{{ detailFormData.always_show ? "是" : "否" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="菜单标题" :span="2">
|
||||
{{ detailFormData.title }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="路由参数" :span="2">
|
||||
{{ detailFormData.params }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="是否固定路由" :span="2">
|
||||
<ElTag :type="detailFormData.affix ? 'success' : 'danger'">
|
||||
{{ detailFormData.affix ? "是" : "否" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag :type="detailFormData.status === '0' ? 'success' : 'danger'">
|
||||
{{ detailFormData.status === "0" ? "启用" : "停用" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="排序" :span="2">
|
||||
{{ detailFormData.order }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="4">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</template>
|
||||
</FaDescriptions>
|
||||
</template>
|
||||
|
||||
<!-- 新增、编辑表单 -->
|
||||
<template v-else>
|
||||
<ElForm
|
||||
<FaForm
|
||||
:key="menuFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
:model="formData"
|
||||
v-model="formData"
|
||||
:items="menuDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
label-width="100px"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<ElFormItem
|
||||
v-if="formData.type !== MenuTypeEnum.CATALOG"
|
||||
label="父级菜单"
|
||||
prop="parent_id"
|
||||
>
|
||||
<!-- 父级菜单(条件显示) -->
|
||||
<template v-if="formData.type !== MenuTypeEnum.CATALOG" #parent_id>
|
||||
<ElTreeSelect
|
||||
v-model="formData.parent_id"
|
||||
placeholder="选择上级菜单"
|
||||
@@ -199,17 +134,10 @@
|
||||
<ElText v-if="createParentLocked" type="info" size="small" class="block mt-1">
|
||||
在菜单下仅可新增按钮,父级已固定
|
||||
</ElText>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem label="菜单名称" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入菜单名称" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="菜单标题" prop="title">
|
||||
<ElInput v-model="formData.title" placeholder="请输入菜单标题" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="菜单类型" prop="type">
|
||||
<!-- 菜单类型(动态枚举按钮组) -->
|
||||
<template #type>
|
||||
<ElRadioGroup v-model="formData.type" @change="handleMenuTypeChange">
|
||||
<ElRadio
|
||||
v-if="allowedMenuTypeValues.includes(MenuTypeEnum.CATALOG)"
|
||||
@@ -236,9 +164,10 @@
|
||||
外链
|
||||
</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem label="终端" prop="client">
|
||||
<!-- 终端(禁用态+提示) -->
|
||||
<template #client>
|
||||
<ElRadioGroup v-model="formData.client" :disabled="createParentLocked">
|
||||
<ElRadio :value="MenuClientEnum.PC">PC 桌面</ElRadio>
|
||||
<ElRadio :value="MenuClientEnum.APP">APP 移动</ElRadio>
|
||||
@@ -246,91 +175,41 @@
|
||||
<ElText v-if="createParentLocked" type="info" size="small" class="block mt-1">
|
||||
子级终端与父菜单一致
|
||||
</ElText>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem v-if="formData.type == MenuTypeEnum.EXTLINK" label="外链地址" prop="path">
|
||||
<!-- 外链地址(条件显示) -->
|
||||
<template v-if="formData.type == MenuTypeEnum.EXTLINK" #extlink_path>
|
||||
<ElInput v-model="formData.route_path" placeholder="请输入外链完整路径" />
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem v-if="formData.type !== MenuTypeEnum.BUTTON" prop="route_name">
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
路由名称
|
||||
<ElTooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
如果需要开启缓存,需保证页面 defineOptions 中的 name 与此处一致,建议使用驼峰。
|
||||
</template>
|
||||
<ElIcon class="ml-1 cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
</template>
|
||||
<!-- 路由名称(Tooltip标签) -->
|
||||
<template v-if="formData.type !== MenuTypeEnum.BUTTON" #route_name>
|
||||
<ElInput v-model="formData.route_name" placeholder="请输入路由名称" />
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem
|
||||
<!-- 路由路径(Tooltip标签) -->
|
||||
<template
|
||||
v-if="formData.type == MenuTypeEnum.CATALOG || formData.type == MenuTypeEnum.MENU"
|
||||
prop="route_path"
|
||||
#route_path
|
||||
>
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
路由路径
|
||||
<ElTooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
定义应用中不同页面对应的 URL 路径,目录需以 /
|
||||
开头,菜单项不用。例如:系统管理目录 /system,系统管理下的用户管理菜单 user。
|
||||
</template>
|
||||
<ElIcon class="ml-1 cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
</template>
|
||||
<ElInput v-model="formData.route_path" placeholder="请输入路由路径,如:/system" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem v-if="formData.type == MenuTypeEnum.MENU" prop="component">
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
组件路径
|
||||
<ElTooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
组件页面完整路径,相对于 src/views/,如 system/user/index,缺省后缀 .vue
|
||||
</template>
|
||||
<ElIcon class="ml-1 cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<!-- 组件路径(Tooltip+prepend/append) -->
|
||||
<template v-if="formData.type == MenuTypeEnum.MENU" #component_path>
|
||||
<ElInput
|
||||
v-model="formData.component_path"
|
||||
placeholder="请输入组件路径,如system/user/index"
|
||||
:style="'width: 95%'"
|
||||
>
|
||||
<template v-if="formData.type == MenuTypeEnum.MENU" #prepend>src/views/</template>
|
||||
<template v-if="formData.type == MenuTypeEnum.MENU" #append>.vue</template>
|
||||
<template #prepend>src/views/</template>
|
||||
<template #append>.vue</template>
|
||||
</ElInput>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem v-if="formData.type == MenuTypeEnum.MENU">
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
路由参数
|
||||
<ElTooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
组件页面使用 `useRoute().query.参数名` 获取路由参数值。
|
||||
</template>
|
||||
<ElIcon class="ml-1 cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div
|
||||
<!-- 路由参数(动态键值编辑器) -->
|
||||
<template v-if="formData.type == MenuTypeEnum.MENU" #params>
|
||||
<template
|
||||
v-if="
|
||||
!formData.params || (Array.isArray(formData.params) && formData.params.length === 0)
|
||||
"
|
||||
@@ -338,16 +217,12 @@
|
||||
<ElButton type="success" plain @click="formData.params = [{ key: '', value: '' }]">
|
||||
添加路由参数
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div v-for="(item, index) in formData.params" :key="index">
|
||||
<ElInput v-model="item.key" placeholder="参数名" :style="'width: 100px'" />
|
||||
|
||||
<span class="mx-1">=</span>
|
||||
|
||||
<ElInput v-model="item.value" placeholder="参数值" :style="'width: 100px'" />
|
||||
|
||||
<ElIcon
|
||||
v-if="formData.params.indexOf(item) === formData.params.length - 1"
|
||||
class="ml-2 cursor-pointer color-[var(--el-color-success)]"
|
||||
@@ -364,90 +239,52 @@
|
||||
<DeleteFilled />
|
||||
</ElIcon>
|
||||
</div>
|
||||
</div>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem v-if="formData.type !== MenuTypeEnum.BUTTON">
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
是否隐藏
|
||||
<ElTooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
选择"是", 菜单中隐藏
|
||||
<br />
|
||||
选择"否",菜单中显示。
|
||||
<br />
|
||||
</template>
|
||||
<ElIcon class="ml-1 cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</template>
|
||||
<!-- 是否隐藏(Tooltip标签) -->
|
||||
<template v-if="formData.type !== MenuTypeEnum.BUTTON" #hidden>
|
||||
<ElRadioGroup v-model="formData.hidden">
|
||||
<ElRadio :value="true">是</ElRadio>
|
||||
<ElRadio :value="false">否</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem
|
||||
<!-- 始终显示(Tooltip标签) -->
|
||||
<template
|
||||
v-if="formData.type === MenuTypeEnum.CATALOG || formData.type === MenuTypeEnum.MENU"
|
||||
#always_show
|
||||
>
|
||||
<template #label>
|
||||
<div class="flex-y-center">
|
||||
始终显示
|
||||
<ElTooltip placement="bottom" effect="light">
|
||||
<template #content>
|
||||
选择"是",即使目录或菜单下只有一个子节点,也会显示父节点。
|
||||
<br />
|
||||
选择"否",如果目录或菜单下只有一个子节点,则只显示该子节点,隐藏父节点。
|
||||
<br />
|
||||
如果是叶子节点,请选择"否"。
|
||||
</template>
|
||||
<ElIcon class="ml-1 cursor-pointer">
|
||||
<QuestionFilled />
|
||||
</ElIcon>
|
||||
</ElTooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<ElRadioGroup v-model="formData.always_show">
|
||||
<ElRadio :value="true">是</ElRadio>
|
||||
<ElRadio :value="false">否</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem v-if="formData.type === MenuTypeEnum.MENU" label="缓存页面">
|
||||
<!-- 缓存页面(v-if) -->
|
||||
<template v-if="formData.type === MenuTypeEnum.MENU" #keep_alive>
|
||||
<ElRadioGroup v-model="formData.keep_alive">
|
||||
<ElRadio :value="true">开启</ElRadio>
|
||||
<ElRadio :value="false">关闭</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem label="排序" prop="order">
|
||||
<ElInputNumber v-model="formData.order" controls-position="right" :min="1" />
|
||||
</ElFormItem>
|
||||
|
||||
<!-- 权限标识 -->
|
||||
<ElFormItem
|
||||
<!-- 权限标识(v-if) -->
|
||||
<template
|
||||
v-if="formData.type == MenuTypeEnum.BUTTON || formData.type === MenuTypeEnum.MENU"
|
||||
label="权限标识"
|
||||
prop="perm"
|
||||
#permission
|
||||
>
|
||||
<ElInput v-model="formData.permission" placeholder="请输入权限标识,如sys:user:add" />
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem v-if="formData.type !== MenuTypeEnum.BUTTON" label="图标" prop="icon">
|
||||
<!-- 图标选择器 -->
|
||||
<!-- 图标(v-if) -->
|
||||
<template v-if="formData.type !== MenuTypeEnum.BUTTON" #icon>
|
||||
<FaIconSelect v-model="formData.icon" />
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem
|
||||
<!-- 重定向(v-if, 动态placeholder) -->
|
||||
<template
|
||||
v-if="formData.type == MenuTypeEnum.CATALOG || formData.type === MenuTypeEnum.MENU"
|
||||
label="重定向"
|
||||
prop="redirect"
|
||||
:required="formData.type === MenuTypeEnum.CATALOG"
|
||||
#redirect
|
||||
>
|
||||
<ElInput
|
||||
v-model="formData.redirect"
|
||||
@@ -457,49 +294,16 @@
|
||||
: '可选,请输入重定向路由'
|
||||
"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</template>
|
||||
|
||||
<ElFormItem v-if="formData.type != MenuTypeEnum.BUTTON" label="常驻标签栏" prop="affix">
|
||||
<!-- 常驻标签栏(v-if) -->
|
||||
<template v-if="formData.type != MenuTypeEnum.BUTTON" #affix>
|
||||
<ElRadioGroup v-model="formData.affix">
|
||||
<ElRadio :value="true">是</ElRadio>
|
||||
<ElRadio :value="false">否</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="状态" prop="status">
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">禁用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="描述" prop="description">
|
||||
<ElInput
|
||||
v-model="formData.description"
|
||||
:rows="4"
|
||||
:maxlength="100"
|
||||
show-word-limit
|
||||
type="textarea"
|
||||
placeholder="请输入描述"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<!-- 详情弹窗不需要确定按钮的提交逻辑 -->
|
||||
<ElButton
|
||||
v-if="dialogVisible.type !== 'detail'"
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDrawer>
|
||||
</div>
|
||||
@@ -511,8 +315,7 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import { h, ref, reactive, computed, nextTick, onMounted } from "vue";
|
||||
import { CirclePlusFilled, DeleteFilled, QuestionFilled } from "@element-plus/icons-vue";
|
||||
import { CirclePlusFilled, DeleteFilled } from "@element-plus/icons-vue";
|
||||
import { useAppStore } from "@stores/modules/app.store";
|
||||
import { useUserStore } from "@stores/modules/user.store";
|
||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||
@@ -524,15 +327,8 @@ import MenuAPI, {
|
||||
} from "@/api/module_system/menu";
|
||||
import { MenuClientEnum, MenuTypeEnum } from "@/enums/system/menu.enum";
|
||||
import { formatTree } from "@utils/common";
|
||||
import MenuRouteIcon from "@/components/others/fa-menu-routeIcon/index.vue";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDrawer from "@/components/modal/fa-drawer/index.vue";
|
||||
import FaIconSelect from "@/components/others/fa-icon-select/index.vue";
|
||||
import { ElMessage, ElMessageBox, ElTag, ElTooltip } from "element-plus";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
|
||||
@@ -697,6 +493,108 @@ const createParentLocked = ref(false);
|
||||
|
||||
const detailFormData = ref<MenuTable>({});
|
||||
|
||||
const menuDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "编号", prop: "id" },
|
||||
{ label: "菜单名称", prop: "name" },
|
||||
{ label: "菜单类型", prop: "type", slot: "type" },
|
||||
{ label: "终端", prop: "client", slot: "client" },
|
||||
{ label: "图标", prop: "icon", slot: "icon" },
|
||||
{ label: "权限标识", prop: "permission" },
|
||||
{ label: "路由名称", prop: "route_name" },
|
||||
{ label: "路由路径", prop: "route_path" },
|
||||
{ label: "组件路径", prop: "component_path" },
|
||||
{ label: "重定向", prop: "redirect" },
|
||||
{ label: "父级编号", prop: "parent_id" },
|
||||
{ label: "父级菜单", prop: "parent_name" },
|
||||
{
|
||||
label: "是否缓存",
|
||||
prop: "keep_alive",
|
||||
tag: {
|
||||
map: { true: { type: "success", text: "是" }, false: { type: "danger", text: "否" } },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "是否显示",
|
||||
prop: "hidden",
|
||||
tag: {
|
||||
map: { true: { type: "success", text: "是" }, false: { type: "danger", text: "否" } },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "是否显示根路由",
|
||||
prop: "always_show",
|
||||
tag: {
|
||||
map: { true: { type: "success", text: "是" }, false: { type: "danger", text: "否" } },
|
||||
},
|
||||
},
|
||||
{ label: "菜单标题", prop: "title" },
|
||||
{ label: "路由参数", prop: "params" },
|
||||
{
|
||||
label: "是否固定路由",
|
||||
prop: "affix",
|
||||
tag: {
|
||||
map: { true: { type: "success", text: "是" }, false: { type: "danger", text: "否" } },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "排序", prop: "order" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
{ label: "描述", prop: "description", span: 4 },
|
||||
];
|
||||
|
||||
// 菜单新增/编辑表单 —— 简单字段走 items,条件渲染/Tooltip标签/复杂控件走插槽
|
||||
const menuDialogFormItems: FormItem[] = [
|
||||
{ key: "parent_id", label: "父级菜单", type: "input" /* #parent_id 插槽 */ },
|
||||
{ key: "name", label: "菜单名称", type: "input", props: { placeholder: "请输入菜单名称" } },
|
||||
{ key: "title", label: "菜单标题", type: "input", props: { placeholder: "请输入菜单标题" } },
|
||||
{ key: "type", label: "菜单类型", type: "input" /* #type 插槽 */ },
|
||||
{ key: "client", label: "终端", type: "input" /* #client 插槽 */ },
|
||||
{ key: "extlink_path", label: "外链地址", type: "input" /* #extlink_path 插槽 */ },
|
||||
{ key: "route_name", label: "路由名称", type: "input" /* #route_name 插槽 */ },
|
||||
{ key: "route_path", label: "路由路径", type: "input" /* #route_path 插槽 */ },
|
||||
{ key: "component_path", label: "组件路径", type: "input" /* #component_path 插槽 */ },
|
||||
{ key: "params", label: "路由参数", type: "input" /* #params 插槽 */ },
|
||||
{ key: "hidden", label: "是否隐藏", type: "input" /* #hidden 插槽 */ },
|
||||
{ key: "always_show", label: "始终显示", type: "input" /* #always_show 插槽 */ },
|
||||
{ key: "keep_alive", label: "缓存页面", type: "input" /* #keep_alive 插槽 */ },
|
||||
{ key: "order", label: "排序", type: "number", props: { controlsPosition: "right", min: 1 } },
|
||||
{ key: "permission", label: "权限标识", type: "input" /* #permission 插槽 */ },
|
||||
{ key: "icon", label: "图标", type: "input" /* #icon 插槽 */ },
|
||||
{ key: "redirect", label: "重定向", type: "input" /* #redirect 插槽 */ },
|
||||
{ key: "affix", label: "常驻标签栏", type: "input" /* #affix 插槽 */ },
|
||||
{
|
||||
key: "status",
|
||||
label: "状态",
|
||||
type: "radiogroup",
|
||||
props: {
|
||||
options: [
|
||||
{ label: "启用", value: "0" },
|
||||
{ label: "禁用", value: "1" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "description",
|
||||
label: "描述",
|
||||
type: "input",
|
||||
props: {
|
||||
type: "textarea",
|
||||
rows: 4,
|
||||
maxlength: 100,
|
||||
showWordLimit: true,
|
||||
placeholder: "请输入描述",
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const formData = ref<MenuForm>({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
@@ -1004,7 +902,8 @@ const initialFormData: MenuForm = {
|
||||
client: MenuClientEnum.PC,
|
||||
};
|
||||
|
||||
const dataFormRef = ref();
|
||||
const dataFormRef = ref<InstanceType<typeof FaForm> | null>(null);
|
||||
const menuFormRenderKey = ref(0);
|
||||
|
||||
async function resetForm() {
|
||||
if (dataFormRef.value) {
|
||||
@@ -1052,6 +951,7 @@ async function handleOpenDialog(
|
||||
} else {
|
||||
dialogVisible.title = "新增菜单";
|
||||
formData.value = { ...initialFormData };
|
||||
menuFormRenderKey.value += 1;
|
||||
if (parentRow?.id != null) {
|
||||
formData.value.parent_id = parentRow.id;
|
||||
formData.value.client = (parentRow.client as MenuClientEnum) || menuClientTab.value;
|
||||
@@ -1075,7 +975,7 @@ function handleMenuTypeChange() {
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.clearValidate("redirect");
|
||||
if (formData.value.type === MenuTypeEnum.CATALOG) {
|
||||
dataFormRef.value?.validateField("redirect").catch(() => {});
|
||||
dataFormRef.value?.validateField("redirect")?.catch(() => {});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
:perm-patch="['module_system:notice:patch']"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="handleOpenDialog('create')"
|
||||
@export="openExportModal"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
@more="handleMoreClick"
|
||||
/>
|
||||
@@ -71,105 +71,74 @@
|
||||
width="920px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="4" border label-width="120px">
|
||||
<ElDescriptionsItem label="标题" :span="2">
|
||||
{{ detailFormData.notice_title }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="类型" :span="2">
|
||||
<ElTag :type="detailFormData.notice_type === '1' ? 'primary' : 'warning'">
|
||||
{{ noticeTypeLabel(detailFormData.notice_type) }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag :type="detailFormData.status === '0' ? 'success' : 'danger'">
|
||||
{{ detailFormData.status === "0" ? "启用" : "停用" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="2">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="内容" :span="4">
|
||||
<div class="notice-html-preview">
|
||||
<template v-if="detailHasRenderableContent">
|
||||
<div v-html="detailContentHtml" />
|
||||
</template>
|
||||
<p v-else class="notice-html-empty">暂无内容</p>
|
||||
</div>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建人" :span="2">
|
||||
{{ detailFormData.created_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新人" :span="2">
|
||||
{{ detailFormData.updated_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="4"
|
||||
:data="detailFormData"
|
||||
:items="noticeDetailItems"
|
||||
label-width="120px"
|
||||
max-height="75vh"
|
||||
>
|
||||
<template #notice_type="{ row }">
|
||||
<ElTag :type="row?.notice_type === '1' ? 'primary' : 'warning'">
|
||||
{{ noticeTypeLabel(row?.notice_type as string) }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<template #notice_content>
|
||||
<div class="notice-html-preview">
|
||||
<template v-if="detailHasRenderableContent">
|
||||
<div v-html="detailContentHtml" />
|
||||
</template>
|
||||
<p v-else class="notice-html-empty">暂无内容</p>
|
||||
</div>
|
||||
</template>
|
||||
</FaDescriptions>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<!-- FaForm + items + 栅格;弹窗内关闭内置提交/重置,仍用底部按钮 -->
|
||||
<FaForm
|
||||
:key="noticeFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="noticeDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="100"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
<template #notice_content>
|
||||
<FaWangEditor
|
||||
:model-value="formData.notice_content ?? ''"
|
||||
height="min(38vh, 280px)"
|
||||
placeholder="请输入公告内容,支持完整排版与插入..."
|
||||
:exclude-keys="[]"
|
||||
@update:model-value="(v: string) => (formData.notice_content = v)"
|
||||
/>
|
||||
</template>
|
||||
</FaForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton
|
||||
v-if="dialogVisible.type !== 'detail'"
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<!-- FaForm + items + 栅格;弹窗内关闭内置提交/重置,仍用底部按钮 -->
|
||||
<FaForm
|
||||
:key="noticeFormRenderKey"
|
||||
scrollbar
|
||||
max-height="75vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="noticeDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="100"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
<template #notice_content>
|
||||
<FaWangEditor
|
||||
:model-value="formData.notice_content ?? ''"
|
||||
height="min(38vh, 280px)"
|
||||
placeholder="请输入公告内容,支持完整排版与插入..."
|
||||
:exclude-keys="[]"
|
||||
@update:model-value="(v: string) => (formData.notice_content = v)"
|
||||
/>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="noticeExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -179,30 +148,21 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, reactive, nextTick, onMounted } from "vue";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { IObject } from "@/components/modal/types";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import NoticeAPI, {
|
||||
type NoticeForm,
|
||||
type NoticePageQuery,
|
||||
type NoticeTable,
|
||||
} from "@/api/module_system/notice";
|
||||
import { ElMessage, ElMessageBox, ElTag } from "element-plus";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
import { useDictStore, useNoticeStore } from "@stores/index";
|
||||
import UserTableSelect from "@views/module_system/user/components/UserTableSelect.vue";
|
||||
import FaWangEditor from "@/components/forms/fa-wang-editor/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
|
||||
defineOptions({
|
||||
name: "Notice",
|
||||
@@ -436,6 +396,25 @@ const noticeExportContentConfig = computed(() => ({
|
||||
|
||||
const detailFormData = ref<NoticeTable>({});
|
||||
|
||||
const noticeDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "标题", prop: "notice_title" },
|
||||
{ label: "类型", prop: "notice_type", slot: "notice_type" },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "描述", prop: "description" },
|
||||
{ label: "内容", prop: "notice_content", slot: "notice_content", span: 4 },
|
||||
{ label: "创建人", prop: "created_by.name" },
|
||||
{ label: "更新人", prop: "updated_by.name" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
];
|
||||
|
||||
/** 详情富文本 HTML(用于预览) */
|
||||
const detailContentHtml = computed({
|
||||
get: () => detailFormData.value.notice_content ?? "",
|
||||
@@ -545,7 +524,7 @@ const initialFormData: NoticeForm = {
|
||||
description: undefined,
|
||||
};
|
||||
|
||||
const exportModalVisible = ref(false);
|
||||
const { exportVisible, openExport } = useImportExport();
|
||||
|
||||
function buildNoticeReplaceParams(p: NoticeSearchForm): Record<string, unknown> {
|
||||
return {
|
||||
@@ -743,10 +722,6 @@ async function handleMoreClick(status: string) {
|
||||
}
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await dictStore.getDict(["sys_notice_type"]);
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
:perm-delete="['module_system:param:delete']"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="handleOpenDialog('create')"
|
||||
@export="openExportModal"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
/>
|
||||
</template>
|
||||
@@ -60,81 +60,49 @@
|
||||
width="640px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptionsItem label="配置名称" :span="2">
|
||||
{{ detailFormData.config_name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="系统内置" :span="2">
|
||||
<ElTag v-if="detailFormData.config_type" type="success">是</ElTag>
|
||||
<ElTag v-else type="danger">否</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="配置键" :span="2">
|
||||
{{ detailFormData.config_key }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="配置值" :span="2">
|
||||
{{ detailFormData.config_value }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="2">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="4"
|
||||
:data="detailFormData"
|
||||
:items="paramDetailItems"
|
||||
max-height="75vh"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<FaForm
|
||||
:key="paramFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="paramDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="100"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #config_type>
|
||||
<ElRadioGroup v-model="formData.config_type">
|
||||
<ElRadio :value="true">是</ElRadio>
|
||||
<ElRadio :value="false">否</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton
|
||||
v-if="dialogVisible.type !== 'detail'"
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<FaForm
|
||||
:key="paramFormRenderKey"
|
||||
scrollbar
|
||||
max-height="75vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="paramDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="100"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #config_type>
|
||||
<ElRadioGroup v-model="formData.config_type">
|
||||
<ElRadio :value="true">是</ElRadio>
|
||||
<ElRadio :value="false">否</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="paramExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -144,17 +112,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, reactive } from "vue";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { IObject } from "@/components/modal/types";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import ParamsAPI, {
|
||||
@@ -162,7 +123,6 @@ import ParamsAPI, {
|
||||
type ConfigPageQuery,
|
||||
type ConfigTable,
|
||||
} from "@/api/module_system/params";
|
||||
import { ElMessage, ElMessageBox, ElTag } from "element-plus";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
import { useConfigStore } from "@stores";
|
||||
@@ -362,6 +322,23 @@ const paramExportContentConfig = computed(() => ({
|
||||
|
||||
const detailFormData = ref<ConfigTable>({} as ConfigTable);
|
||||
|
||||
const paramDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "配置名称", prop: "config_name" },
|
||||
{
|
||||
label: "系统内置",
|
||||
prop: "config_type",
|
||||
tag: {
|
||||
map: { true: { type: "success", text: "是" }, false: { type: "danger", text: "否" } },
|
||||
},
|
||||
},
|
||||
{ label: "配置键", prop: "config_key" },
|
||||
{ label: "配置值", prop: "config_value" },
|
||||
{ label: "描述", prop: "description" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
];
|
||||
|
||||
const formData = ref<ConfigForm>({
|
||||
id: undefined,
|
||||
config_name: "",
|
||||
@@ -441,7 +418,7 @@ const initialFormData: ConfigForm = {
|
||||
description: "",
|
||||
};
|
||||
|
||||
const exportModalVisible = ref(false);
|
||||
const { exportVisible, openExport } = useImportExport();
|
||||
|
||||
async function handleSearchBarSearch(params: ParamSearchForm) {
|
||||
await searchBarRef.value?.validate?.();
|
||||
@@ -460,8 +437,8 @@ function onResetSearch() {
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, initialFormData);
|
||||
}
|
||||
|
||||
@@ -597,10 +574,6 @@ async function handleBatchDelete() {
|
||||
batchDeleting.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<!-- 岗位管理:Art + useTable;操作列前 3 个为 ArtButtonTable,其余收入「更多」下拉 -->
|
||||
<!-- 岗位管理:FA + useTable;操作列前 3 个为 ArtButtonTable,其余收入「更多」下拉 -->
|
||||
<template>
|
||||
<div class="fa-full-height">
|
||||
<FaSearchBar
|
||||
@@ -46,7 +46,7 @@
|
||||
:perm-patch="['module_system:position:patch']"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="handleOpenDialog('create')"
|
||||
@export="openExportModal"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
@more="handleMoreClick"
|
||||
/>
|
||||
@@ -71,84 +71,49 @@
|
||||
width="720px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptionsItem label="岗位名称" :span="2">
|
||||
{{ detailFormData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="排序" :span="2">
|
||||
{{ detailFormData.order }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag v-if="detailFormData.status === '0'" type="success">启用</ElTag>
|
||||
<ElTag v-else type="danger">停用</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建人" :span="2">
|
||||
{{ detailFormData.created_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新人" :span="2">
|
||||
{{ detailFormData.updated_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="4">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="4"
|
||||
:data="detailFormData"
|
||||
:items="positionDetailItems"
|
||||
max-height="75vh"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<FaForm
|
||||
:key="positionFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="positionDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton
|
||||
v-if="dialogVisible.type !== 'detail'"
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<FaForm
|
||||
scrollbar
|
||||
max-height="75vh"
|
||||
:key="positionFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="positionDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="positionExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -158,18 +123,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, reactive, nextTick } from "vue";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import ArtButtonTable from "@/components/forms/fa-button-table/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { IObject } from "@/components/modal/types";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import PositionAPI, {
|
||||
@@ -177,15 +134,6 @@ import PositionAPI, {
|
||||
type PositionPageQuery,
|
||||
type PositionTable,
|
||||
} from "@/api/module_system/position";
|
||||
import {
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
ElTag,
|
||||
ElTooltip,
|
||||
ElDropdown,
|
||||
ElDropdownMenu,
|
||||
ElDropdownItem,
|
||||
} from "element-plus";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { useUserStore } from "@stores";
|
||||
import UserTableSelect from "@views/module_system/user/components/UserTableSelect.vue";
|
||||
@@ -281,7 +229,7 @@ function formatPositionOperationCell(
|
||||
const inlineNodes = inline.map((a) =>
|
||||
h(ElTooltip, { content: a.label, placement: "top" }, () =>
|
||||
h("span", { class: "inline-flex" }, [
|
||||
h(ArtButtonTable, {
|
||||
h(FaButtonTable, {
|
||||
type: a.artType,
|
||||
icon: a.icon,
|
||||
onClick: a.run,
|
||||
@@ -305,7 +253,7 @@ function formatPositionOperationCell(
|
||||
default: () =>
|
||||
h(ElTooltip, { content: "更多", placement: "top" }, () =>
|
||||
h("span", { class: "inline-flex align-middle" }, [
|
||||
h(ArtButtonTable, {
|
||||
h(FaButtonTable, {
|
||||
type: "more",
|
||||
onClick: () => {},
|
||||
}),
|
||||
@@ -531,6 +479,24 @@ const positionExportContentConfig = computed(() => ({
|
||||
|
||||
const detailFormData = ref<PositionTable>({});
|
||||
|
||||
const positionDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "岗位名称", prop: "name" },
|
||||
{ label: "排序", prop: "order" },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "创建人", prop: "created_by.name" },
|
||||
{ label: "更新人", prop: "updated_by.name" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
{ label: "描述", prop: "description", span: 4 },
|
||||
];
|
||||
|
||||
const formData = ref<PositionForm>({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
@@ -599,11 +565,7 @@ const positionDialogFormItems = computed<FormItem[]>(() => [
|
||||
},
|
||||
]);
|
||||
const submitLoading = ref(false);
|
||||
const exportModalVisible = ref(false);
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
const { exportVisible, openExport } = useImportExport();
|
||||
|
||||
async function handleSearchBarSearch(params: PositionSearchForm) {
|
||||
await searchBarRef.value?.validate?.();
|
||||
@@ -633,8 +595,8 @@ function onResetSearch() {
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, initialFormData);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@
|
||||
:perm-patch="['module_system:role:patch']"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="handleOpenDialog('create')"
|
||||
@export="openExportModal"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
@more="handleMoreClick"
|
||||
/>
|
||||
@@ -62,99 +62,65 @@
|
||||
width="720px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="4" border>
|
||||
<ElDescriptionsItem label="角色名称" :span="2">
|
||||
{{ detailFormData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="排序" :span="2">
|
||||
{{ detailFormData.order }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="角色编码" :span="2">
|
||||
{{ detailFormData.code }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="数据权限" :span="2">
|
||||
<ElTag v-if="detailFormData.data_scope === 1" type="primary">仅本人数据权限</ElTag>
|
||||
<ElTag v-else-if="detailFormData.data_scope === 2" type="info">本部门数据权限</ElTag>
|
||||
<ElTag v-else-if="detailFormData.data_scope === 3" type="warning">
|
||||
本部门及以下数据权限
|
||||
<FaDescriptions
|
||||
:column="4"
|
||||
:data="detailFormData"
|
||||
:items="roleDetailItems"
|
||||
max-height="75vh"
|
||||
>
|
||||
<template #data_scope="{ row }">
|
||||
<ElTag v-if="row?.data_scope === 1" type="primary">仅本人数据权限</ElTag>
|
||||
<ElTag v-else-if="row?.data_scope === 2" type="info">本部门数据权限</ElTag>
|
||||
<ElTag v-else-if="row?.data_scope === 3" type="warning">本部门及以下数据权限</ElTag>
|
||||
<ElTag v-else-if="row?.data_scope === 4" type="success">全部数据权限</ElTag>
|
||||
<ElTag v-else type="danger">自定义数据权限</ElTag>
|
||||
</template>
|
||||
<template #depts="{ row }">
|
||||
<template v-if="row?.depts && (row.depts as any[]).length > 0">
|
||||
<ElTag
|
||||
v-for="dept in row.depts as any[]"
|
||||
:key="dept.id"
|
||||
type="info"
|
||||
:style="'margin-right: 4px; margin-bottom: 4px'"
|
||||
>
|
||||
{{ dept.name }}
|
||||
</ElTag>
|
||||
<ElTag v-else-if="detailFormData.data_scope === 4" type="success">全部数据权限</ElTag>
|
||||
<ElTag v-else type="danger">自定义数据权限</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="所属部门" :span="2">
|
||||
<template v-if="detailFormData.depts && detailFormData.depts.length > 0">
|
||||
<ElTag
|
||||
v-for="dept in detailFormData.depts"
|
||||
:key="dept.id"
|
||||
type="info"
|
||||
:style="'margin-right: 4px; margin-bottom: 4px'"
|
||||
>
|
||||
{{ dept.name }}
|
||||
</ElTag>
|
||||
</template>
|
||||
<span v-else :style="'color: var(--el-text-color-placeholder)'">-</span>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag :type="detailFormData.status === '0' ? 'success' : 'danger'">
|
||||
{{ detailFormData.status === "0" ? "启用" : "停用" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="4">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
<span v-else :style="'color: var(--el-text-color-placeholder)'">-</span>
|
||||
</template>
|
||||
</FaDescriptions>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<FaForm
|
||||
:key="roleFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="roleDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton
|
||||
v-if="dialogVisible.type !== 'detail'"
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<FaForm
|
||||
:key="roleFormRenderKey"
|
||||
scrollbar
|
||||
max-height="75vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="roleDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<template #status>
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDialog>
|
||||
|
||||
@@ -167,7 +133,7 @@
|
||||
/>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="roleExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -177,18 +143,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, reactive } from "vue";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
import type { IObject } from "@/components/modal/types";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import RoleAPI, {
|
||||
@@ -196,7 +155,6 @@ import RoleAPI, {
|
||||
type RoleTable,
|
||||
type TablePageQuery,
|
||||
} from "@/api/module_system/role";
|
||||
import { ElMessage, ElMessageBox, ElTag } from "element-plus";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import { useUserStore } from "@stores";
|
||||
import PermissonDrawer from "./components/PermissonDrawer.vue";
|
||||
@@ -553,6 +511,25 @@ const roleExportContentConfig = computed(() => ({
|
||||
|
||||
const detailFormData = ref<RoleTable>({} as RoleTable);
|
||||
|
||||
const roleDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "角色名称", prop: "name" },
|
||||
{ label: "排序", prop: "order" },
|
||||
{ label: "角色编码", prop: "code" },
|
||||
{ label: "数据权限", prop: "data_scope", slot: "data_scope" },
|
||||
{ label: "所属部门", prop: "depts", slot: "depts" },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
{ label: "描述", prop: "description", span: 4 },
|
||||
];
|
||||
|
||||
const formData = ref<RoleForm>({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
@@ -649,7 +626,7 @@ const initialFormData: RoleForm = {
|
||||
description: undefined,
|
||||
};
|
||||
|
||||
const exportModalVisible = ref(false);
|
||||
const { exportVisible, openExport } = useImportExport();
|
||||
|
||||
async function handleSearchBarSearch(params: RoleSearchForm) {
|
||||
await searchBarRef.value?.validate?.();
|
||||
@@ -667,8 +644,8 @@ function onResetSearch() {
|
||||
}
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, initialFormData);
|
||||
}
|
||||
|
||||
@@ -765,10 +742,6 @@ async function handleMoreClick(status: string) {
|
||||
// 用户取消
|
||||
}
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -58,86 +58,45 @@
|
||||
width="640px"
|
||||
dialog-class="crud-embed-dialog"
|
||||
modal-class="crud-embed-dialog"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="租户名称" :span="2">
|
||||
{{ detailFormData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="租户编码" :span="2">
|
||||
{{ detailFormData.code }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态">
|
||||
<ElTag :type="detailFormData.status === '0' ? 'success' : 'danger'">
|
||||
{{ detailFormData.status === "0" ? "正常" : "禁用" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="开始时间">
|
||||
{{ detailFormData.start_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="结束时间">
|
||||
{{ detailFormData.end_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="2">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</ElScrollbar>
|
||||
<FaDescriptions
|
||||
:column="2"
|
||||
:data="detailFormData"
|
||||
:items="tenantDetailItems"
|
||||
max-height="75vh"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElScrollbar max-height="75vh" :view-style="{ overflowX: 'hidden' }">
|
||||
<FaForm
|
||||
:key="tenantFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="tenantDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
/>
|
||||
</ElScrollbar>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer" :style="'padding-right: var(--el-dialog-padding-primary)'">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton
|
||||
v-if="dialogVisible.type !== 'detail'"
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
<FaForm
|
||||
:key="tenantFormRenderKey"
|
||||
scrollbar
|
||||
max-height="75vh"
|
||||
ref="dataFormRef"
|
||||
v-model="formData"
|
||||
:items="tenantDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
/>
|
||||
</template>
|
||||
</FaDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { h, computed, ref, reactive } from "vue";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import ArtButtonTable from "@/components/forms/fa-button-table/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDialog from "@/components/modal/fa-dialog/index.vue";
|
||||
import FaForm from "@/components/forms/fa-form/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import TenantAPI, {
|
||||
@@ -146,15 +105,6 @@ import TenantAPI, {
|
||||
type TenantTable,
|
||||
type TenantUpdateForm,
|
||||
} from "@/api/module_system/tenant";
|
||||
import {
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
ElTag,
|
||||
ElTooltip,
|
||||
ElDropdown,
|
||||
ElDropdownMenu,
|
||||
ElDropdownItem,
|
||||
} from "element-plus";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
|
||||
defineOptions({
|
||||
@@ -240,7 +190,7 @@ function formatTenantOperationCell(
|
||||
const inlineNodes = inline.map((a) =>
|
||||
h(ElTooltip, { content: a.label, placement: "top" }, () =>
|
||||
h("span", { class: "inline-flex" }, [
|
||||
h(ArtButtonTable, {
|
||||
h(FaButtonTable, {
|
||||
type: a.artType,
|
||||
icon: a.icon,
|
||||
onClick: a.run,
|
||||
@@ -264,7 +214,7 @@ function formatTenantOperationCell(
|
||||
default: () =>
|
||||
h(ElTooltip, { content: "更多", placement: "top" }, () =>
|
||||
h("span", { class: "inline-flex align-middle" }, [
|
||||
h(ArtButtonTable, {
|
||||
h(FaButtonTable, {
|
||||
type: "more",
|
||||
onClick: () => {},
|
||||
}),
|
||||
@@ -445,6 +395,24 @@ const {
|
||||
|
||||
const detailFormData = ref<TenantTable>({ code: "", name: "", status: "0" });
|
||||
|
||||
const tenantDetailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] =
|
||||
[
|
||||
{ label: "租户名称", prop: "name" },
|
||||
{ label: "租户编码", prop: "code" },
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
span: 1,
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "正常" }, "1": { type: "danger", text: "禁用" } },
|
||||
},
|
||||
},
|
||||
{ label: "开始时间", prop: "start_time", span: 1 },
|
||||
{ label: "结束时间", prop: "end_time", span: 1 },
|
||||
{ label: "描述", prop: "description" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
];
|
||||
|
||||
const currentEditId = ref<number | null>(null);
|
||||
|
||||
const formData = ref<TenantForm>({
|
||||
@@ -574,8 +542,8 @@ const tenantDialogFormItems = computed<FormItem[]>(() => [
|
||||
]);
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, initialFormData);
|
||||
currentEditId.value = null;
|
||||
}
|
||||
|
||||
@@ -67,8 +67,8 @@
|
||||
:import-loading="uploadLoading"
|
||||
:delete-loading="batchDeleting"
|
||||
@add="handleOpenDialog('create')"
|
||||
@import="openImportModal"
|
||||
@export="openExportModal"
|
||||
@import="openImport"
|
||||
@export="openExport"
|
||||
@delete="handleBatchDelete"
|
||||
@more="handleMoreClick"
|
||||
/>
|
||||
@@ -95,121 +95,60 @@
|
||||
:title="dialogVisible.title"
|
||||
append-to-body
|
||||
:size="drawerSize"
|
||||
@close="handleCloseDialog"
|
||||
:form-mode="dialogVisible.type"
|
||||
:confirm-loading="submitLoading"
|
||||
@cancel="handleCloseDialog"
|
||||
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
|
||||
>
|
||||
<template v-if="dialogVisible.type === 'detail'">
|
||||
<ElDescriptions :column="2" border>
|
||||
<ElDescriptionsItem label="编号" :span="2">
|
||||
{{ detailFormData.id }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="头像" :span="2">
|
||||
<template v-if="detailFormData.avatar">
|
||||
<ElAvatar :src="detailFormData.avatar" size="small"></ElAvatar>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElAvatar icon="UserFilled" size="small"></ElAvatar>
|
||||
</template>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="账号" :span="2">
|
||||
{{ detailFormData.username }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="用户名" :span="2">
|
||||
{{ detailFormData.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="性别" :span="2">
|
||||
<ElTag v-if="detailFormData.gender === '0'" type="success">男</ElTag>
|
||||
<ElTag v-else-if="detailFormData.gender === '1'" type="warning">女</ElTag>
|
||||
<FaDescriptions
|
||||
:column="2"
|
||||
:data="detailFormData"
|
||||
:items="userDetailItems"
|
||||
:scrollbar="false"
|
||||
>
|
||||
<!-- 头像 → 自定义渲染 -->
|
||||
<template #avatar="{ row }">
|
||||
<ElAvatar v-if="row?.avatar" :src="row?.avatar as string" size="small" />
|
||||
<ElAvatar v-else icon="UserFilled" size="small" />
|
||||
</template>
|
||||
<!-- 性别 → 三种状态 Tag -->
|
||||
<template #gender="{ row }">
|
||||
<ElTag v-if="row?.gender === '0'" type="success">男</ElTag>
|
||||
<ElTag v-else-if="row?.gender === '1'" type="warning">女</ElTag>
|
||||
<ElTag v-else type="info">未知</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="部门" :span="2">
|
||||
{{ detailFormData.dept ? detailFormData.dept.name : "" }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="角色" :span="2">
|
||||
</template>
|
||||
<!-- 角色 → 数组 join 渲染 -->
|
||||
<template #roles="{ row }">
|
||||
{{ row?.roles ? (row.roles as any[]).map((item: any) => item.name).join("、") : "" }}
|
||||
</template>
|
||||
<!-- 岗位 → 数组 join 渲染 -->
|
||||
<template #positions="{ row }">
|
||||
{{
|
||||
detailFormData.roles ? detailFormData.roles.map((item) => item.name).join("、") : ""
|
||||
}}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="岗位" :span="2">
|
||||
{{
|
||||
detailFormData.positions
|
||||
? detailFormData.positions.map((item) => item.name).join("、")
|
||||
row?.positions
|
||||
? (row.positions as any[]).map((item: any) => item.name).join("、")
|
||||
: ""
|
||||
}}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="邮箱" :span="2">
|
||||
{{ detailFormData.email }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="手机号" :span="2">
|
||||
{{ detailFormData.mobile }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="是否超管" :span="2">
|
||||
<ElTag :type="detailFormData.is_superuser ? 'success' : 'info'">
|
||||
{{ detailFormData.is_superuser ? "是" : "否" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态" :span="2">
|
||||
<ElTag :type="detailFormData.status === '0' ? 'success' : 'danger'">
|
||||
{{ detailFormData.status === "0" ? "启用" : "停用" }}
|
||||
</ElTag>
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="上次登录时间" :span="2">
|
||||
{{ detailFormData.last_login }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建人" :span="2">
|
||||
{{ detailFormData.created_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新人" :span="2">
|
||||
{{ detailFormData.updated_by?.name }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="创建时间" :span="2">
|
||||
{{ detailFormData.created_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="更新时间" :span="2">
|
||||
{{ detailFormData.updated_time }}
|
||||
</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="描述" :span="4">
|
||||
{{ detailFormData.description }}
|
||||
</ElDescriptionsItem>
|
||||
</ElDescriptions>
|
||||
</template>
|
||||
</FaDescriptions>
|
||||
</template>
|
||||
<template v-else>
|
||||
<ElForm
|
||||
<FaForm
|
||||
:key="userFormRenderKey"
|
||||
ref="dataFormRef"
|
||||
:model="formData"
|
||||
v-model="formData"
|
||||
:items="userDialogFormItems"
|
||||
:rules="rules"
|
||||
label-suffix=":"
|
||||
label-width="100px"
|
||||
:label-width="'auto'"
|
||||
label-position="right"
|
||||
:span="24"
|
||||
:gutter="16"
|
||||
:show-reset="false"
|
||||
:show-submit="false"
|
||||
class="crud-dialog-art-form"
|
||||
>
|
||||
<ElFormItem label="账号" prop="username">
|
||||
<ElInput
|
||||
v-model="formData.username"
|
||||
:disabled="!!formData.id"
|
||||
placeholder="请输入账号"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="用户名" prop="name">
|
||||
<ElInput v-model="formData.name" placeholder="请输入用户名" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="性别" prop="gender">
|
||||
<ElSelect v-model="formData.gender" placeholder="请选择性别">
|
||||
<ElOption label="男" value="0" />
|
||||
<ElOption label="女" value="1" />
|
||||
<ElOption label="未知" value="2" />
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="手机号" prop="mobile">
|
||||
<ElInput v-model="formData.mobile" placeholder="请输入手机号码" maxlength="11" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="邮箱" prop="email">
|
||||
<ElInput v-model="formData.email" placeholder="请输入邮箱" maxlength="50" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="部门" prop="dept_id">
|
||||
<template #dept_id>
|
||||
<ElTreeSelect
|
||||
v-model="formData.dept_id"
|
||||
placeholder="请选择上级部门"
|
||||
@@ -219,9 +158,8 @@
|
||||
check-strictly
|
||||
:render-after-expand="false"
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="角色" prop="role_ids">
|
||||
</template>
|
||||
<template #role_ids>
|
||||
<ElSelect v-model="formData.role_ids" multiple placeholder="请选择角色">
|
||||
<ElOption
|
||||
v-for="item in roleOptions"
|
||||
@@ -231,9 +169,8 @@
|
||||
:disabled="item.disabled"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="岗位" prop="position_ids">
|
||||
</template>
|
||||
<template #position_ids>
|
||||
<ElSelect v-model="formData.position_ids" multiple placeholder="请选择岗位">
|
||||
<ElOption
|
||||
v-for="item in positionOptions"
|
||||
@@ -243,60 +180,13 @@
|
||||
:disabled="item.disabled"
|
||||
/>
|
||||
</ElSelect>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem v-if="!formData.id" label="密码" prop="password">
|
||||
<ElInput
|
||||
v-model="formData.password"
|
||||
placeholder="请输入密码"
|
||||
type="password"
|
||||
show-password
|
||||
clearable
|
||||
/>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="是否超管" prop="is_superuser">
|
||||
<ElSwitch v-model="formData.is_superuser" />
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="状态" prop="status">
|
||||
<ElRadioGroup v-model="formData.status">
|
||||
<ElRadio value="0">启用</ElRadio>
|
||||
<ElRadio value="1">停用</ElRadio>
|
||||
</ElRadioGroup>
|
||||
</ElFormItem>
|
||||
|
||||
<ElFormItem label="描述" prop="description">
|
||||
<ElInput
|
||||
v-model="formData.description"
|
||||
:rows="4"
|
||||
:maxlength="100"
|
||||
show-word-limit
|
||||
type="textarea"
|
||||
placeholder="请输入描述"
|
||||
/>
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
</template>
|
||||
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<ElButton @click="handleCloseDialog">取消</ElButton>
|
||||
<ElButton
|
||||
v-if="dialogVisible.type === 'create' || dialogVisible.type === 'update'"
|
||||
type="primary"
|
||||
:loading="submitLoading"
|
||||
@click="handleSubmit"
|
||||
>
|
||||
确定
|
||||
</ElButton>
|
||||
<ElButton v-else type="primary" @click="handleCloseDialog">确定</ElButton>
|
||||
</div>
|
||||
</template>
|
||||
</FaForm>
|
||||
</template>
|
||||
</FaDrawer>
|
||||
|
||||
<FaImportDialog
|
||||
v-model="importModalVisible"
|
||||
v-model="importVisible"
|
||||
:content-config="userImportContentConfig"
|
||||
default-template-file-name="user_import_template.xlsx"
|
||||
:loading="uploadLoading"
|
||||
@@ -304,7 +194,7 @@
|
||||
/>
|
||||
|
||||
<FaExportDialog
|
||||
v-model="exportModalVisible"
|
||||
v-model="exportVisible"
|
||||
:content-config="userExportContentConfig"
|
||||
:query-params="exportQueryParams"
|
||||
:page-data="data"
|
||||
@@ -319,20 +209,14 @@ defineOptions({
|
||||
inheritAttrs: false,
|
||||
});
|
||||
|
||||
import { h, ref, reactive, computed, nextTick } from "vue";
|
||||
import { UserFilled } from "@element-plus/icons-vue";
|
||||
import { useAppStore } from "@stores/modules/app.store";
|
||||
import { DeviceEnum } from "@/enums/settings/device.enum";
|
||||
import { ResultEnum } from "@/enums/api/result.enum";
|
||||
import { useTable } from "@/hooks/core/useTable";
|
||||
import FaTable from "@/components/tables/fa-table/index.vue";
|
||||
import FaTableHeader from "@/components/tables/fa-table-header/index.vue";
|
||||
import FaTableHeaderLeft from "@/components/tables/fa-table-header-left/index.vue";
|
||||
import FaSearchBar from "@/components/forms/fa-search-bar/index.vue";
|
||||
import { useImportExport } from "@/hooks/core/useImportExport";
|
||||
import type { SearchFormItem } from "@/components/forms/fa-search-bar/index.vue";
|
||||
import FaDrawer from "@/components/modal/fa-drawer/index.vue";
|
||||
import FaImportDialog from "@/components/modal/fa-import-dialog/index.vue";
|
||||
import FaExportDialog from "@/components/modal/fa-export-dialog/index.vue";
|
||||
import type { FormItem } from "@/components/forms/fa-form/index.vue";
|
||||
import type { IContentConfig, IObject } from "@/components/modal/types";
|
||||
import UserAPI, {
|
||||
type UserForm,
|
||||
@@ -347,6 +231,7 @@ import DeptTree from "./components/DeptTree.vue";
|
||||
import UserTableSelect from "./components/UserTableSelect.vue";
|
||||
import { useUserStore } from "@stores";
|
||||
import { ElMessage, ElMessageBox, ElTag, ElAvatar } from "element-plus";
|
||||
import type { DescriptionsItem } from "@/components/others/fa-descriptions/index.vue";
|
||||
import { useAuth } from "@/hooks/core/useAuth";
|
||||
import type { ColumnOption } from "@/types/component";
|
||||
import { renderTableOperationCell, type TableOperationAction } from "@utils/table";
|
||||
@@ -455,7 +340,8 @@ function formatUserOperationCell(row: UserInfo, ctx: Parameters<typeof buildUser
|
||||
});
|
||||
}
|
||||
|
||||
const dataFormRef = ref();
|
||||
const dataFormRef = ref<InstanceType<typeof FaForm> | null>(null);
|
||||
const userFormRenderKey = ref(0);
|
||||
const submitLoading = ref(false);
|
||||
const uploadLoading = ref(false);
|
||||
const batchDeleting = ref(false);
|
||||
@@ -465,10 +351,113 @@ const drawerSize = computed(() => (appStore.device === DeviceEnum.DESKTOP ? "450
|
||||
const deptOptions = ref<OptionType[]>();
|
||||
const roleOptions = ref<Array<{ value: number; label: string; disabled?: boolean }>>();
|
||||
const positionOptions = ref<Array<{ value: number; label: string; disabled?: boolean }>>();
|
||||
const importModalVisible = ref(false);
|
||||
const exportModalVisible = ref(false);
|
||||
const { importVisible, exportVisible, openImport, openExport } = useImportExport();
|
||||
const detailFormData = ref<UserInfo>({});
|
||||
|
||||
// 用户详情描述项配置 —— 数据驱动 + 关键字段用具名插槽覆盖
|
||||
const userDetailItems: DescriptionsItem[] = [
|
||||
{ label: "编号", prop: "id" },
|
||||
{ label: "头像", prop: "avatar", slot: "avatar" }, // 自定义插槽渲染
|
||||
{ label: "账号", prop: "username" },
|
||||
{ label: "用户名", prop: "name" },
|
||||
{ label: "性别", prop: "gender", slot: "gender" }, // 三种状态 Tag
|
||||
{ label: "部门", prop: "dept.name" }, // 嵌套属性 a.b.c
|
||||
{ label: "角色", prop: "roles", slot: "roles" }, // 数组 join 渲染
|
||||
{ label: "岗位", prop: "positions", slot: "positions" }, // 数组 join 渲染
|
||||
{ label: "邮箱", prop: "email" },
|
||||
{ label: "手机号", prop: "mobile" },
|
||||
{
|
||||
label: "是否超管",
|
||||
prop: "is_superuser",
|
||||
tag: {
|
||||
map: { true: { type: "success", text: "是" }, false: { type: "info", text: "否" } },
|
||||
},
|
||||
},
|
||||
{
|
||||
label: "状态",
|
||||
prop: "status",
|
||||
tag: {
|
||||
map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } },
|
||||
},
|
||||
},
|
||||
{ label: "上次登录时间", prop: "last_login" },
|
||||
{ label: "创建人", prop: "created_by.name" },
|
||||
{ label: "更新人", prop: "updated_by.name" },
|
||||
{ label: "创建时间", prop: "created_time" },
|
||||
{ label: "更新时间", prop: "updated_time" },
|
||||
{ label: "描述", prop: "description", span: 4 },
|
||||
];
|
||||
|
||||
// 用户新增/编辑表单配置 —— 三个复杂字段(部门树、角色多选、岗位多选)用插槽渲染
|
||||
const userDialogFormItems = computed<FormItem[]>(() => [
|
||||
{
|
||||
key: "username",
|
||||
label: "账号",
|
||||
type: "input",
|
||||
props: { placeholder: "请输入账号", disabled: !!formData.value.id },
|
||||
},
|
||||
{ key: "name", label: "用户名", type: "input", props: { placeholder: "请输入用户名" } },
|
||||
{
|
||||
key: "gender",
|
||||
label: "性别",
|
||||
type: "select",
|
||||
props: {
|
||||
placeholder: "请选择性别",
|
||||
options: [
|
||||
{ label: "男", value: "0" },
|
||||
{ label: "女", value: "1" },
|
||||
{ label: "未知", value: "2" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "mobile",
|
||||
label: "手机号",
|
||||
type: "input",
|
||||
props: { placeholder: "请输入手机号码", maxlength: 11 },
|
||||
},
|
||||
{
|
||||
key: "email",
|
||||
label: "邮箱",
|
||||
type: "input",
|
||||
props: { placeholder: "请输入邮箱", maxlength: 50 },
|
||||
},
|
||||
{ key: "dept_id", label: "部门", type: "input" /* 实际渲染由 #dept_id 插槽接管 */ },
|
||||
{ key: "role_ids", label: "角色", type: "input" /* 实际渲染由 #role_ids 插槽接管 */ },
|
||||
{ key: "position_ids", label: "岗位", type: "input" /* 实际渲染由 #position_ids 插槽接管 */ },
|
||||
{
|
||||
key: "password",
|
||||
label: "密码",
|
||||
type: "input",
|
||||
hidden: !!formData.value.id,
|
||||
props: { placeholder: "请输入密码", type: "password", showPassword: true, clearable: true },
|
||||
},
|
||||
{ key: "is_superuser", label: "是否超管", type: "switch" },
|
||||
{
|
||||
key: "status",
|
||||
label: "状态",
|
||||
type: "radiogroup",
|
||||
props: {
|
||||
options: [
|
||||
{ label: "启用", value: "0" },
|
||||
{ label: "停用", value: "1" },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "description",
|
||||
label: "描述",
|
||||
type: "input",
|
||||
props: {
|
||||
type: "textarea",
|
||||
rows: 4,
|
||||
maxlength: 100,
|
||||
showWordLimit: true,
|
||||
placeholder: "请输入描述",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
const searchForm = ref<UserSearchForm>({
|
||||
username: undefined,
|
||||
name: undefined,
|
||||
@@ -836,21 +825,13 @@ async function handleDeptNodeClick() {
|
||||
await getData();
|
||||
}
|
||||
|
||||
function openImportModal() {
|
||||
importModalVisible.value = true;
|
||||
}
|
||||
|
||||
function openExportModal() {
|
||||
exportModalVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleImportUpload(formDataUpload: FormData) {
|
||||
uploadLoading.value = true;
|
||||
try {
|
||||
const response = await UserAPI.importUser(formDataUpload);
|
||||
if (response.data.code === ResultEnum.SUCCESS) {
|
||||
ElMessage.success(`${response.data.msg},${response.data.data}`);
|
||||
importModalVisible.value = false;
|
||||
importVisible.value = false;
|
||||
await refreshData();
|
||||
} else {
|
||||
ElMessage.error(response.data.msg || "导入失败");
|
||||
@@ -893,7 +874,9 @@ async function handleOpenDialog(type: "create" | "update" | "detail", id?: numbe
|
||||
}
|
||||
} else {
|
||||
dialogVisible.title = "新增用户";
|
||||
Object.assign(formData.value, initialFormData);
|
||||
formData.value.id = undefined;
|
||||
userFormRenderKey.value += 1;
|
||||
}
|
||||
dialogVisible.visible = true;
|
||||
await nextTick();
|
||||
@@ -928,7 +911,7 @@ async function handleOpenDialog(type: "create" | "update" | "detail", id?: numbe
|
||||
}
|
||||
|
||||
async function handleSubmit() {
|
||||
dataFormRef.value.validate(async (valid: boolean) => {
|
||||
dataFormRef.value?.validate(async (valid: boolean) => {
|
||||
if (!valid) return;
|
||||
submitLoading.value = true;
|
||||
const id = formData.value.id;
|
||||
|
||||
@@ -778,8 +778,8 @@ const initialFormData: Partial<NodeForm> = {
|
||||
};
|
||||
|
||||
async function resetForm() {
|
||||
dataFormRef.value?.ref?.resetFields();
|
||||
dataFormRef.value?.ref?.clearValidate();
|
||||
dataFormRef.value?.resetFields();
|
||||
dataFormRef.value?.clearValidate();
|
||||
Object.assign(formData, initialFormData);
|
||||
argsList.value = [];
|
||||
kwargsList.value = [];
|
||||
@@ -886,7 +886,7 @@ function handleOpenExecuteDialog(row: NodeTable) {
|
||||
function handleCloseExecuteDialog() {
|
||||
executeDialogVisible.value = false;
|
||||
currentExecuteNode.value = null;
|
||||
executeFormRef.value?.ref?.resetFields();
|
||||
executeFormRef.value?.resetFields();
|
||||
}
|
||||
|
||||
async function handleExecuteNode() {
|
||||
|
||||
@@ -454,8 +454,8 @@ const rules: FormRules = {
|
||||
function resetForm() {
|
||||
Object.assign(formData.value, defaultForm());
|
||||
editingId.value = null;
|
||||
formRef.value?.ref?.resetFields();
|
||||
formRef.value?.ref?.clearValidate();
|
||||
formRef.value?.resetFields();
|
||||
formRef.value?.clearValidate();
|
||||
}
|
||||
|
||||
function handleCloseDialog() {
|
||||
|
||||
Reference in New Issue
Block a user