You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

821 lines
28 KiB
Django/Jinja

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

{# =============================================================================
# {{ function_name }} 页面
# 架构FaSearchBarWithAudit + FaTableHeader + FaTable + FaDialog
# 与 module_example/demo 完全对齐
# 页面路由:/{{ menu_route_first_segment }}/{{ module_name }}
# ============================================================================= #}
<template>
<div class="fa-full-height">
<FaSearchBarWithAudit
v-show="showSearchBar"
ref="searchBarRef"
v-model="searchForm"
:items="businessSearchItems"
:rules="searchBarRules"
:is-expand="false"
:show-expand="true"
:show-reset="true"
:show-search="true"
:disabled-search="false"
:default-expanded="false"
@search="handleSearch"
@reset="onResetSearch"
/>
<ElCard
shadow="hover"
class="fa-table-card"
:style="{ 'margin-top': showSearchBar ? '12px' : '0' }"
>
<FaTableHeader
v-model:columns="columnChecks"
v-model:showSearchBar="showSearchBar"
:loading="loading"
@refresh="refreshData"
>
<template #left>
<FaTableHeaderLeft
:remove-ids="selectedIds"
:perm-create="['{{ permission_prefix }}:create']"
:perm-import="['{{ permission_prefix }}:import']"
:perm-export="['{{ permission_prefix }}:export']"
:perm-delete="['{{ permission_prefix }}:delete']"
:perm-patch="['{{ permission_prefix }}:patch']"
:delete-loading="batchDeleting"
:create-loading="createLoading"
@add="handleAdd"
@import="openImport"
@export="openExport"
@delete="handleBatchDelete"
@more="runBatchStatus"
/>
</template>
</FaTableHeader>
<FaTable
ref="faTableRef"
:loading="loading"
:data="data"
:columns="columns"
:pagination="pagination"
@selection-change="onTableSelectionChange"
@pagination:size-change="handleSizeChange"
@pagination:current-change="handleCurrentChange"
/>
</ElCard>
<FaDialog
v-model="dialogVisible.visible"
:title="dialogVisible.title"
width="920px"
dialog-class="crud-embed-dialog"
modal-class="crud-embed-dialog"
:form-mode="dialogVisible.type"
:confirm-loading="submitLoading"
@cancel="handleCloseDialog"
@confirm="dialogVisible.type === 'detail' ? handleCloseDialog() : handleSubmit()"
>
<template v-if="dialogVisible.type === 'detail'">
<FaDescriptions
:column="4"
:data="detailFormData"
:items="detailItems"
max-height="70vh"
/>
</template>
<template v-else>
<FaForm
:key="formRenderKey"
scrollbar
max-height="70vh"
ref="dataFormRef"
v-model="formData"
:items="dialogFormItems"
: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"
/>
{% if table.sub %}
<ElDivider>{{ table.sub_table.function_name }}列表</ElDivider>
<div class="sub-table-section">
<div class="flex justify-between items-center mb-2">
<ElButton size="small" type="primary" @click="addSubRow">
新增{{ table.sub_table.function_name }}
</ElButton>
</div>
<ElTable :data="subTableData" size="small" border>
{% for col in table.sub_table.columns %}
{% if col.is_list and col.column_name != table.sub_table.pk_column.column_name %}
{% set col_comment = col.column_comment if col.column_comment else col.column_name %}
<ElTableColumn prop="{{ col.column_name }}" label="{{ col_comment }}" />
{% endif %}
{% endfor %}
<ElTableColumn label="操作" width="80" fixed="right">
<template #default="{ $index }">
<ElButton type="danger" size="small" link @click="removeSubRow($index)">
删除
</ElButton>
</template>
</ElTableColumn>
</ElTable>
</div>
{% endif %}
</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>
</template>
</FaDialog>
<FaImportDialog
v-model="importVisible"
:content-config="importContentConfig"
default-template-file-name="{{ module_name }}_import_template.xlsx"
@upload="handleCrudImportUpload"
/>
<FaExportDialog
v-model="exportVisible"
:content-config="exportContentConfig"
:query-params="exportQueryParams"
:page-data="data"
:selection-data="selectedRows"
/>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted } from "vue";
import { useAuth } from "@/hooks/core/useAuth";
import { renderTableOperationCell, type TableOperationAction } from "@/utils/table";
import { useTable } from "@/hooks/core/useTable";
import { useImportExport } from "@/hooks/core/useImportExport";
import { useCrudDialog } from "@/hooks/core/useCrudDialog";
import { useTableSelection } from "@/hooks/core/useTableSelection";
import { confirmDelete, confirmBatchDelete, confirmAction } from "@/hooks/core/useConfirm";
import { stripPaginationParams } from "@/utils/query";
import type { IContentConfig, IObject } from "@/components/modal/types";
import type { AuditSearchFormParams } from "@/components/forms/fa-search-bar/auditSearchFormItems";
import type { FormItem } from "@/components/forms/fa-form/index.vue";
import type { ColumnOption } from "@/types/component";
import {{ class_name }}API, {
type {{ class_name }}Form,
type {{ class_name }}PageQuery,
type {{ class_name }}Table,
} from "@/api/{{ package_name }}/{{ module_name }}";
import { ResultEnum } from "@/enums/api/result.enum";
import { ElMessage } from "element-plus";
{% if table.sub or has_dict_column %}
import { computed } from "vue";
{% endif %}
{% if has_dict_column %}
import { useDictStore } from "@/stores/modules/dict";
{% endif %}
{% if has_image_column %}
import { h } from "vue";
{% endif %}
defineOptions({
name: "{{ class_name }}",
inheritAttrs: false,
});
const { hasAuth } = useAuth();
{% if has_dict_column %}
const dictStore = useDictStore();
{% endif %}
// 常量定义
const STATUS_OPTIONS = [
{ label: "启用", value: 0 },
{ label: "停用", value: 1 },
] as const;
const createInitialFormData = (): {{ class_name }}Form => ({
{% for column in columns %}
{% if column.is_insert or column.is_edit %}
{% if column.column_name not in ['uuid', 'created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id', 'tenant_id'] and column.column_name != pk_column_name %}
{% if column.column_name == "status" %}
{{ column.column_name }}: {% if column.python_type == "str" %}"0"{% else %}0{% endif %},
{% elif column.python_type == "bool" or column.html_type == "switch" %}
{{ column.column_name }}: false,
{% else %}
{{ column.column_name }}: undefined,
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
});
type {{ class_name }}SearchFormParams = {
{% for column in columns %}
{% if column.is_query and column.column_name not in ['created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id'] and column.column_name != pk_column_name %}
{{ column.column_name }}?: string;
{% endif %}
{% endfor %}
} & AuditSearchFormParams;
const searchForm = ref<{{ class_name }}SearchFormParams>({
{% for column in columns %}
{% if column.is_query and column.column_name not in ['created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id'] and column.column_name != pk_column_name %}
{{ column.column_name }}: undefined,
{% endif %}
{% endfor %}
created_id: undefined,
updated_id: undefined,
created_time: [],
updated_time: [],
});
/** 搜索区域默认展开展示 */
const showSearchBar = ref(true);
const searchBarRef = ref<{ validate: () => Promise<boolean> } | null>(null);
const searchBarRules: Record<string, unknown> = {};
{% if has_dict_column %}
/** 业务搜索项(审计四字段由 FaSearchBarWithAudit 自动追加) */
const businessSearchItemsRaw = computed(() => [
{% else %}
/** 业务搜索项(审计四字段由 FaSearchBarWithAudit 自动追加) */
const businessSearchItems = computed(() => [
{% endif %}
{% for column in columns %}
{% if column.is_query and column.column_name not in ['created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id'] and column.column_name != pk_column_name %}
{% set dict_type = column.dict_type %}
{% set column_comment = column.column_comment if column.column_comment else '' %}
{% set parentheseIndex = column_comment.find("") %}
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
{% if column.column_name == "status" %}
{
label: "状态",
key: "status",
type: "select",
props: {
placeholder: "请选择状态",
options: STATUS_OPTIONS,
clearable: true,
},
span: 6,
},
{% elif column.html_type == "input" %}
{
label: "{{ comment }}",
key: "{{ column.column_name }}",
type: "input",
placeholder: "请输入{{ comment }}",
clearable: true,
span: 6,
},
{% elif (column.html_type == "select" or column.html_type == "radio") and dict_type != "" %}
{
label: "{{ comment }}",
key: "{{ column.column_name }}",
type: "select",
props: {
placeholder: "请选择{{ comment }}",
options: [],
clearable: true,
},
span: 6,
},
{% elif column.html_type == "datetime" %}
{
label: "{{ comment }}",
key: "{{ column.column_name }}",
type: "date-picker",
props: {
type: "date",
valueFormat: "YYYY-MM-DD",
clearable: true,
placeholder: "请选择{{ comment }}",
},
span: 6,
},
{% endif %}
{% endif %}
{% endfor %}
]);
{% if has_dict_column %}
const businessSearchItems = computed(() => {
const items = businessSearchItemsRaw.value.map(item => ({ ...item }));
{% for column in columns %}
{% if column.is_query and column.dict_type and (column.html_type == "select" or column.html_type == "radio") %}
const item_{{ column.column_name }} = items.find((i: any) => i.key === "{{ column.column_name }}");
if (item_{{ column.column_name }} && item_{{ column.column_name }}.props) {
item_{{ column.column_name }}.props.options = dictStore.getDictArray("{{ column.dict_type }}").map(
(d: { dict_label: string; dict_value: string }) => ({
label: d.dict_label,
value: d.dict_value,
})
);
}
{% endif %}
{% endfor %}
return items;
});
{% endif %}
const faTableRef = ref<{ elTableRef?: { clearSelection: () => void } } | null>(null);
const { selectedRows, selectedIds, batchDeleting, onTableSelectionChange } =
useTableSelection<{{ class_name }}Table>();
const createLoading = ref(false);
const PK = "{{ pk_column_name }}" as const;
const {
columns,
columnChecks,
data,
loading,
pagination,
searchParams,
getData,
replaceSearchParams,
resetSearchParams,
handleSizeChange,
handleCurrentChange,
refreshData,
refreshCreate,
refreshUpdate,
refreshRemove,
} = useTable({
core: {
apiFn: {{ class_name }}API.get{{ class_name }}List,
apiParams: {
page_no: 1,
page_size: 10,
},
columnsFactory: (): ColumnOption<{{ class_name }}Table>[] => [
{ type: "globalIndex", width: 56, label: "序号" },
{ type: "selection", width: 48, fixed: "left" },
{% for column in columns %}
{% if column.is_list and column.column_name not in ['is_deleted', 'deleted_time', 'deleted_id'] and column.column_name != pk_column_name %}
{% set column_comment = column.column_comment if column.column_comment else '' %}
{% set parentheseIndex = column_comment.find("") %}
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
{% if column.column_name == "status" %}
{
prop: "status",
label: "状态",
width: 88,
status: {
0: { type: "success", text: "启用" },
1: { type: "info", text: "停用" },
},
},
{% elif column.column_name == "created_id" %}
{
prop: "created_by",
label: "创建人",
minWidth: 100,
formatter: (row: {{ class_name }}Table) => row.created_by?.name ?? "—",
},
{% elif column.column_name == "updated_id" %}
{
prop: "updated_by",
label: "更新人",
minWidth: 100,
formatter: (row: {{ class_name }}Table) => row.updated_by?.name ?? "—",
},
{% elif column.column_name == "created_time" %}
{ prop: "created_time", label: "创建时间", width: 168, showOverflowTooltip: true },
{% elif column.column_name == "updated_time" %}
{ prop: "updated_time", label: "更新时间", width: 168, showOverflowTooltip: true },
{% elif column.python_type == "bool" or column.html_type == "checkbox" %}
{
prop: "{{ column.column_name }}",
label: "{{ comment }}",
width: 80,
status: {
true: { type: "success", text: "是" },
false: { type: "danger", text: "否" },
},
},
{% elif column.html_type == "imageUpload" %}
{
prop: "{{ column.column_name }}",
label: "{{ comment }}",
minWidth: 120,
formatter: (row: {{ class_name }}Table) => {
if (!row.{{ column.column_name }}) return h("span", { class: "text-g-400" }, "—");
return h("el-image", {
src: row.{{ column.column_name }},
style: "width: 48px; height: 48px; border-radius: 4px; object-fit: cover;",
fit: "cover",
previewSrcList: [row.{{ column.column_name }}],
hideOnClickModal: true,
});
},
},
{% elif (column.html_type == "select" or column.html_type == "radio") and column.dict_type %}
{ prop: "{{ column.column_name }}", label: "{{ comment }}", minWidth: 120, showOverflowTooltip: true },
{% else %}
{ prop: "{{ column.column_name }}", label: "{{ comment }}", minWidth: 120, showOverflowTooltip: true },
{% endif %}
{% endif %}
{% endfor %}
{
prop: "operation",
label: "操作",
width: 220,
fixed: "right",
align: "right",
formatter: (row: {{ class_name }}Table) => formatOperationCell(row),
},
],
},
});
const crudCols = computed(() =>
columns.value.map((c: ColumnOption<{{ class_name }}Table>) => {
const t = (c as { type?: string }).type;
return {
prop: c.prop,
label: c.label,
type: t === "selection" ? ("selection" as const) : ("default" as const),
show: true,
};
})
);
const exportQueryParams = computed(() => {
return stripPaginationParams(searchParams as Record<string, unknown>);
});
const importContentConfig = computed<IContentConfig>(() => ({
permPrefix: "{{ permission_prefix }}",
cols: crudCols.value,
indexAction: async () => ({}),
importTemplate: () => {{ class_name }}API.downloadTemplate{{ class_name }}(),
}));
const exportContentConfig = computed(() => ({
permPrefix: "{{ permission_prefix }}",
cols: crudCols.value,
exportsBlobAction: async (params: IObject) => {
const merged = {
...(exportQueryParams.value as unknown as Record<string, unknown>),
...params,
} as unknown as {{ class_name }}PageQuery;
const res = await {{ class_name }}API.export{{ class_name }}(merged);
return res.data as Blob;
},
}));
const { dialogVisible } = useCrudDialog();
const detailFormData = ref<{{ class_name }}Table>({});
const detailItems: import("@/components/others/fa-descriptions/index.vue").DescriptionsItem[] = [
{% for column in columns %}
{% if column.is_list and column.column_name not in ['is_deleted', 'deleted_time', 'deleted_id'] %}
{% set column_comment = column.column_comment if column.column_comment else '' %}
{% set parentheseIndex = column_comment.find("") %}
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
{% if column.column_name == 'status' %}
{ label: "状态", prop: "status", tag: { map: { "0": { type: "success", text: "启用" }, "1": { type: "danger", text: "停用" } } } },
{% elif column.column_name == 'created_id' %}
{ label: "创建人", prop: "created_by.name" },
{% elif column.column_name == 'updated_id' %}
{ label: "更新人", prop: "updated_by.name" },
{% elif column.column_name == 'created_time' %}
{ label: "创建时间", prop: "created_time" },
{% elif column.column_name == 'updated_time' %}
{ label: "更新时间", prop: "updated_time" },
{% else %}
{ label: "{{ comment }}", prop: "{{ column.column_name }}" },
{% endif %}
{% endif %}
{% endfor %}
];
const formData = ref<{{ class_name }}Form>(createInitialFormData());
const rules = reactive({
{% for column in columns %}
{% if column.is_insert or column.is_edit %}
{% if column.column_name not in ['uuid', 'created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id', 'tenant_id'] and column.column_name != pk_column_name %}
{{ column.column_name }}: [{ required: {% if not column.is_nullable %}true{% else %}false{% endif %}, message: "请填写{{ column.column_comment or column.column_name }}", trigger: "blur" }],
{% endif %}
{% endif %}
{% endfor %}
});
const dialogFormItems: FormItem[] = [
{% for column in columns %}
{% if column.is_insert or column.is_edit %}
{% set dict_type = column.dict_type %}
{% set column_comment = column.column_comment if column.column_comment else '' %}
{% set parentheseIndex = column_comment.find("") %}
{% set comment = column_comment[:parentheseIndex] if parentheseIndex != -1 else column_comment %}
{% if column.column_name not in ['uuid', 'created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id', 'tenant_id'] and column.column_name != pk_column_name %}
{% if column.column_name == "status" %}
{
key: "status",
label: "状态",
type: "radiogroup",
props: {
options: [
{ label: "启用", value: 0 },
{ label: "停用", value: 1 },
],
},
},
{% elif column.column_name == "description" %}
{
key: "description",
label: "描述",
type: "input",
props: {
type: "textarea",
rows: 4,
maxlength: 100,
showWordLimit: true,
placeholder: "请输入描述",
},
},
{% elif column.python_type == "int" or column.python_type == "float" or column.python_type == "Decimal" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "number", props: { placeholder: "请输入{{ comment }}"{% if column.python_type == "float" or column.python_type == "Decimal" %}, step: 0.01, precision: 2{% endif %} } },
{% elif column.html_type == "input" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "input", props: { placeholder: "请输入{{ comment }}" } },
{% elif column.html_type == "textarea" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "input", props: { type: "textarea", rows: 4, placeholder: "请输入{{ comment }}" } },
{% elif dict_type != "" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "select", placeholder: "请选择{{ comment }}", options: dictStore.getDictArray("{{ dict_type }}").map(d => ({ label: d.dict_label, value: d.dict_value })) },
{% elif column.html_type == "select" or column.html_type == "radio" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "select", placeholder: "请选择{{ comment }}" },
{% elif column.html_type == "date" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "date", props: { placeholder: "请选择{{ comment }}", valueFormat: "YYYY-MM-DD", style: "width: 100%" } },
{% elif column.html_type == "datetime" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "datetime", props: { placeholder: "请选择{{ comment }}", valueFormat: "YYYY-MM-DD HH:mm:ss", style: "width: 100%" } },
{% elif column.html_type == "switch" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "switch" },
{% elif column.html_type == "checkbox" %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "checkbox" },
{% else %}
{ key: "{{ column.column_name }}", label: "{{ comment }}", type: "input", props: { placeholder: "请输入{{ comment }}" } },
{% endif %}
{% endif %}
{% endif %}
{% endfor %}
];
const dataFormRef = ref<{
resetFields: () => void;
clearValidate: () => void;
validate: (cb: (valid: boolean) => void) => void;
} | null>(null);
const submitLoading = ref(false);
const formRenderKey = ref(0);
const { importVisible, exportVisible, openImport, openExport } = useImportExport();
const handleSearch = async (params: {{ class_name }}SearchFormParams) => {
await searchBarRef.value?.validate();
replaceSearchParams({
{% for column in columns %}
{% if column.is_query and column.column_name not in ['created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id'] and column.column_name != pk_column_name %}
{{ column.column_name }}: params.{{ column.column_name }},
{% endif %}
{% endfor %}
created_id: params.created_id ?? undefined,
updated_id: params.updated_id ?? undefined,
created_time:
Array.isArray(params.created_time) && params.created_time.length === 2
? params.created_time
: undefined,
updated_time:
Array.isArray(params.updated_time) && params.updated_time.length === 2
? params.updated_time
: undefined,
} as Record<string, unknown>);
getData();
};
const onResetSearch = async () => {
searchForm.value = {
{% for column in columns %}
{% if column.is_query and column.column_name not in ['created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id'] and column.column_name != pk_column_name %}
{{ column.column_name }}: undefined,
{% endif %}
{% endfor %}
created_id: undefined,
updated_id: undefined,
created_time: [],
updated_time: [],
};
await resetSearchParams();
};
function buildRowActions(row: {{ class_name }}Table): TableOperationAction[] {
const all: TableOperationAction[] = [
{
key: "detail",
label: "详情",
artType: "view",
perm: "{{ permission_prefix }}:detail",
run: () => void openDetailDialog(row),
},
{
key: "edit",
label: "编辑",
artType: "edit",
icon: "ri:edit-2-line",
perm: "{{ permission_prefix }}:update",
run: () => void openEditDialog("edit", row),
},
{
key: "delete",
label: "删除",
artType: "delete",
icon: "ri:delete-bin-4-line",
perm: "{{ permission_prefix }}:delete",
run: () => deleteRow(row),
},
];
return all.filter((a) => a.perm != null && hasAuth(a.perm));
}
function formatOperationCell(row: {{ class_name }}Table) {
return renderTableOperationCell(buildRowActions(row), {
wrapperClass: "inline-flex flex-wrap items-center justify-end gap-1",
});
}
async function openDetailDialog(row: {{ class_name }}Table) {
if (!row[PK]) return;
const response = await {{ class_name }}API.get{{ class_name }}Detail(row[PK] as number);
dialogVisible.type = "detail";
dialogVisible.title = "详情";
detailFormData.value = response.data.data ?? { ...row };
{% if table.sub %}
subTableData.value = (response.data.data as any)?.{{ sub_rel_list_name }} ?? [];
{% endif %}
dialogVisible.visible = true;
}
async function handleAdd() {
createLoading.value = true;
try {
await openEditDialog("add");
} finally {
createLoading.value = false;
}
}
async function openEditDialog(type: "add" | "edit", row?: {{ class_name }}Table) {
dialogVisible.type = type === "add" ? "create" : "update";
if (type === "add") {
dialogVisible.title = "新增";
Object.assign(formData.value, createInitialFormData());
formData.value[PK] = undefined;
{% if table.sub %}
subTableData.value = [];
{% endif %}
formRenderKey.value += 1;
} else if (row?.[PK]) {
dialogVisible.title = "修改";
formRenderKey.value += 1;
const response = await {{ class_name }}API.get{{ class_name }}Detail(row[PK] as number);
Object.assign(formData.value, response.data.data);
{% if table.sub %}
subTableData.value = (response.data.data as any)?.{{ sub_rel_list_name }} ?? [];
{% endif %}
}
dialogVisible.visible = true;
}
async function resetForm() {
if (dataFormRef.value) {
dataFormRef.value.resetFields();
dataFormRef.value.clearValidate();
}
Object.assign(formData.value, createInitialFormData());
}
async function handleCloseDialog() {
dialogVisible.visible = false;
await resetForm();
}
async function handleSubmit() {
dataFormRef.value?.validate(async (valid: boolean) => {
if (!valid) return;
const submitData = { ...formData.value };
const id = formData.value[PK] as number | undefined;
try {
if (id) {
await {{ class_name }}API.update{{ class_name }}(id, { [PK]: id, ...submitData });
await refreshUpdate();
} else {
await {{ class_name }}API.create{{ class_name }}(submitData);
await refreshCreate();
}
dialogVisible.visible = false;
await resetForm();
} catch (error: unknown) {
console.error(error);
}
});
}
const deleteRow = async (row: {{ class_name }}Table) => {
if (!row[PK]) return;
try {
await confirmDelete(`确定删除该{{ function_name }}吗?此操作不可恢复!`);
await {{ class_name }}API.delete{{ class_name }}([row[PK] as number]);
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
}
};
{% if table.sub %}
const subTableData = ref<any[]>([]);
function addSubRow() {
subTableData.value.push({
{% for col in table.sub_table.columns %}
{% if col.column_name not in ['id', 'uuid', 'status', 'description', 'created_time', 'updated_time', 'created_id', 'updated_id', 'is_deleted', 'deleted_time', 'deleted_id'] and col.column_name != table.sub_table.pk_column.column_name %}
{{ col.column_name }}: undefined,
{% endif %}
{% endfor %}
});
}
function removeSubRow(index: number) {
subTableData.value.splice(index, 1);
}
{% endif %}
async function handleBatchDelete() {
const ids = selectedIds.value;
if (ids.length === 0) return;
try {
await confirmBatchDelete(ids.length);
batchDeleting.value = true;
await {{ class_name }}API.delete{{ class_name }}(ids);
faTableRef.value?.elTableRef?.clearSelection();
await refreshRemove();
} catch {
// 用户取消
} finally {
batchDeleting.value = false;
}
}
async function runBatchStatus(status: number) {
const ids = selectedIds.value;
if (ids.length === 0) {
ElMessage.warning("请先在列表中勾选数据");
return;
}
try {
await confirmAction(
`确认对选中的 ${ids.length} 条数据${status === 0 ? "启用" : "停用"}`,
"批量设置"
);
await {{ class_name }}API.batch{{ class_name }}({ ids, status });
faTableRef.value?.elTableRef?.clearSelection();
await refreshData();
} catch {
// 用户取消
}
}
async function handleCrudImportUpload(formData: FormData) {
try {
const res = await {{ class_name }}API.import{{ class_name }}(formData);
if (res.data.code === ResultEnum.SUCCESS) {
ElMessage.success(res.data.msg || "导入成功");
importVisible.value = false;
await refreshData();
}
} catch (error) {
console.error("[Import]", error);
}
}
onMounted(() => {
getData();
});
</script>
<style lang="scss" scoped></style>