feat: 完善平台总后台模块

This commit is contained in:
2026-07-27 00:18:42 +08:00
parent 073eb89762
commit 642980960e
197 changed files with 22356 additions and 1060 deletions

View File

@@ -0,0 +1,40 @@
<template>
<a-card :title="title" :bordered="false">
<template #extra><a-space><a-button @click="load">刷新</a-button><a-button type="primary" @click="openCreate">新建</a-button></a-space></template>
<a-table :data="list" :loading="loading" :pagination="false" row-key="identity">
<template #columns>
<a-table-column title="业务标识" data-index="identity" :width="220" ellipsis tooltip />
<a-table-column v-for="field in fields" :key="field.key" :title="field.label" :data-index="field.key" ellipsis tooltip />
<a-table-column title="状态" data-index="status" :width="100"><template #cell="{ record }"><a-tag :color="record.status === 'enabled' ? 'green' : 'orange'">{{ record.status }}</a-tag></template></a-table-column>
<a-table-column title="操作" :width="210" fixed="right"><template #cell="{ record }"><a-space><a-button size="mini" @click="openDetail(record)">详情</a-button><a-button size="mini" @click="openEdit(record)">编辑</a-button><a-button size="mini" @click="changeStatus(record)">{{ record.status === 'enabled' ? '停用' : '启用' }}</a-button></a-space></template></a-table-column>
</template>
</a-table>
<div class="pagination"><a-pagination :total="total" :current="page" :page-size="pageSize" show-total @change="changePage" /></div>
</a-card>
<a-drawer :visible="formVisible" :title="editingIdentity ? `编辑${title}` : `新建${title}`" :width="480" @cancel="formVisible = false" @ok="save">
<a-form :model="form" layout="vertical"><a-form-item v-for="field in fields" :key="field.key" :label="field.label" :required="field.required"><a-input v-model="form[field.key]" :placeholder="`请输入${field.label}`" /></a-form-item></a-form>
</a-drawer>
<a-drawer :visible="detailVisible" title="详情" :width="540" @cancel="detailVisible = false"><a-descriptions :column="1" bordered><a-descriptions-item v-for="(value, key) in detail" :key="String(key)" :label="String(key)">{{ value ?? '-' }}</a-descriptions-item></a-descriptions></a-drawer>
</template>
<script setup lang="ts">
import { Message } from '@arco-design/web-vue';
import { onMounted, reactive, ref } from 'vue';
import { resourceApi } from '@/api/resource';
type Field = { key: string; label: string; required?: boolean };
type Row = Record<string, any>;
const props = defineProps<{ title: string; resource: string; fields: Field[] }>();
const loading = ref(false); const page = ref(1); const pageSize = 20; const total = ref(0); const list = ref<Row[]>([]);
const formVisible = ref(false); const detailVisible = ref(false); const editingIdentity = ref(''); const form = reactive<Row>({}); const detail = ref<Row>({});
function resetForm(data?: Row) { props.fields.forEach((field) => { form[field.key] = data?.[field.key] ?? ''; }); }
async function load() { loading.value = true; try { const result = await resourceApi.list<Row>(props.resource, page.value, pageSize); list.value = result.list; total.value = result.total; } catch (error) { Message.error((error as Error).message); } finally { loading.value = false; } }
function openCreate() { editingIdentity.value = ''; resetForm(); formVisible.value = true; }
function openEdit(row: Row) { editingIdentity.value = row.identity; resetForm(row); formVisible.value = true; }
function openDetail(row: Row) { detail.value = row; detailVisible.value = true; }
async function save() { if (props.fields.some((field) => field.required && !form[field.key])) { Message.warning('请填写必填字段'); return; } try { if (editingIdentity.value) await resourceApi.update(props.resource, editingIdentity.value, form); else await resourceApi.create(props.resource, form); Message.success('保存成功'); formVisible.value = false; await load(); } catch (error) { Message.error((error as Error).message); } }
async function changeStatus(row: Row) { try { await resourceApi.updateStatus(props.resource, row.identity, row.status === 'enabled' ? 'disabled' : 'enabled'); await load(); } catch (error) { Message.error((error as Error).message); } }
async function changePage(next: number) { page.value = next; await load(); }
onMounted(load);
</script>
<style scoped lang="less">.pagination { display: flex; justify-content: flex-end; margin-top: 16px; }</style>