yumi-admin/apps/src/views/operate/activity-template.vue
2026-03-31 17:40:10 +08:00

280 lines
7.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import { reactive, ref, watch } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Page } from '@vben/common-ui';
import {
addTemplate,
getTemplateById,
listTemplates,
updateTemplateName,
} from '#/api/legacy/system';
import { formatDate } from '#/views/system/shared';
import {
Button,
Card,
Input,
Modal,
Space,
Table,
message,
} from 'antdv-next';
import ActivityTemplateEditorModal from './components/activity-template-editor-modal.vue';
defineOptions({ name: 'OperateActivityTemplate' });
const route = useRoute();
const router = useRouter();
const loading = ref(false);
const loadMoreLoading = ref(false);
const list = ref<Array<Record<string, any>>>([]);
const notMore = ref(false);
const editorOpen = ref(false);
const activeTemplateId = ref<number | string>('');
const activeRow = ref<Record<string, any> | null>(null);
const nameModalOpen = ref(false);
const nameModalMode = ref<'copy' | 'create' | 'edit'>('create');
const nameValue = ref('');
const nameSaving = ref(false);
const query = reactive({
id: '',
lastId: '',
limit: 20,
});
const columns = [
{ dataIndex: 'id', key: 'id', title: 'ID', width: 120 },
{ dataIndex: 'name', key: 'name', title: '名称', width: 220 },
{ dataIndex: 'createTime', key: 'createTime', title: '创建时间', width: 180 },
{ dataIndex: 'updateTime', key: 'updateTime', title: '修改时间', width: 180 },
{ dataIndex: 'creator', key: 'creator', title: '创建/修改人', width: 200 },
{ dataIndex: 'actions', key: 'actions', title: '操作', width: 220 },
];
function resetList() {
list.value = [];
query.lastId = '';
notMore.value = false;
}
async function loadData(reset = false) {
if (reset) {
resetList();
}
if (reset) {
loading.value = true;
} else {
loadMoreLoading.value = true;
}
try {
const result = (await listTemplates({ ...query })) || [];
list.value = reset ? result : [...list.value, ...result];
notMore.value = result.length < query.limit;
if (list.value.length > 0) {
const last = list.value[list.value.length - 1];
if (last) {
query.lastId = String(last.template?.id || last.id || '');
}
}
} finally {
loading.value = false;
loadMoreLoading.value = false;
}
}
function handleSearch() {
void loadData(true);
}
function openEditor(templateId: number | string) {
activeTemplateId.value = templateId;
editorOpen.value = true;
}
function closeEditor() {
editorOpen.value = false;
activeTemplateId.value = '';
if (route.query.id) {
router.replace({ path: '/operate/manager/activity/template', query: {} });
}
}
function openNameModal(mode: 'copy' | 'create' | 'edit', row?: Record<string, any>) {
activeRow.value = row || null;
nameModalMode.value = mode;
nameValue.value =
mode === 'edit' ? String(row?.template?.name || row?.name || '') : '';
nameModalOpen.value = true;
}
async function handleNameSubmit() {
const value = nameValue.value.trim();
if (!value) {
message.warning('请输入名称');
return;
}
nameSaving.value = true;
try {
if (nameModalMode.value === 'create') {
await addTemplate({
indexTree: [],
name: value,
});
} else if (nameModalMode.value === 'edit') {
await updateTemplateName(activeRow.value?.template?.id || activeRow.value?.id, value);
} else if (activeRow.value) {
let source = activeRow.value.template || activeRow.value;
if (!Array.isArray(source.indexTree)) {
source = await getTemplateById(source.id);
}
await addTemplate({
indexTree: source.indexTree || [],
name: value,
});
}
message.success('保存成功');
nameModalOpen.value = false;
await loadData(true);
} finally {
nameSaving.value = false;
}
}
watch(
() => route.query.id,
(value) => {
if (!value) {
return;
}
activeTemplateId.value = String(value);
editorOpen.value = true;
},
{ immediate: true },
);
void loadData(true);
</script>
<template>
<Page title="活动模版">
<Card>
<Space class="toolbar" wrap>
<Input
v-model:value="query.id"
allow-clear
placeholder="模版ID"
style="width: 220px"
/>
<Button :loading="loading" type="primary" @click="handleSearch">
搜索
</Button>
<Button type="primary" @click="openNameModal('create')">
新增
</Button>
</Space>
<Table
:columns="columns"
:data-source="list"
:loading="loading"
:pagination="false"
row-key="template.id"
:scroll="{ x: 1000 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'id'">
{{ record.template?.id || '-' }}
</template>
<template v-else-if="column.key === 'name'">
{{ record.template?.name || '-' }}
</template>
<template v-else-if="column.key === 'createTime'">
{{ formatDate(record.template?.createTime) }}
</template>
<template v-else-if="column.key === 'updateTime'">
{{ formatDate(record.template?.updateTime) }}
</template>
<template v-else-if="column.key === 'creator'">
<div>创建{{ record.createUserName || '-' }}</div>
<div>修改{{ record.updateUserName || '-' }}</div>
</template>
<template v-else-if="column.key === 'actions'">
<Space wrap>
<Button size="small" type="link" @click="openNameModal('edit', record)">
编辑名称
</Button>
<Button
size="small"
type="link"
@click="openEditor(record.template?.id || record.id)"
>
编辑模版
</Button>
<Button size="small" type="link" @click="openNameModal('copy', record)">
复制模版
</Button>
</Space>
</template>
</template>
</Table>
<div class="load-more">
<span v-if="notMore">已加载全部</span>
<Button
v-else
:loading="loadMoreLoading"
size="small"
@click="loadData(false)"
>
加载更多
</Button>
</div>
</Card>
<ActivityTemplateEditorModal
:open="editorOpen"
:template-id="activeTemplateId"
@close="closeEditor"
@success="loadData(true)"
/>
<Modal
:confirm-loading="nameSaving"
:open="nameModalOpen"
destroy-on-close
:title="nameModalMode === 'create'
? '新增模版'
: nameModalMode === 'edit'
? '编辑模版名称'
: '复制模版'"
@cancel="nameModalOpen = false"
@ok="handleNameSubmit"
>
<Input
v-model:value="nameValue"
:maxlength="200"
placeholder="请输入模版名称"
/>
</Modal>
</Page>
</template>
<style scoped>
.toolbar {
margin-bottom: 16px;
}
.load-more {
display: flex;
justify-content: center;
margin-top: 16px;
}
</style>