|
|
@@ -0,0 +1,242 @@
|
|
|
+<script setup lang="ts">
|
|
|
+import TagEdit from '@/components/TagEdit.vue';
|
|
|
+import { statusOptions } from '@/model/options';
|
|
|
+
|
|
|
+import type { TagModel, TagQuery } from '@/model/system.model';
|
|
|
+
|
|
|
+import { tagDeleteMethod, tagsMethod, tagsSearchMethod, tagUpdateStatusMethod } from '@/request/api/system.api';
|
|
|
+import { usePagination, useRequest } from 'alova/client';
|
|
|
+import { notification } from 'ant-design-vue';
|
|
|
+
|
|
|
+import {
|
|
|
+ type VxeFormListeners,
|
|
|
+ type VxeFormProps,
|
|
|
+ type VxeGridInstance,
|
|
|
+ type VxeGridListeners,
|
|
|
+ type VxeGridProps,
|
|
|
+ VxeUI,
|
|
|
+} from 'vxe-pc-ui';
|
|
|
+
|
|
|
+
|
|
|
+const { data: tags, loading: tagsLoading } = useRequest(tagsSearchMethod, { initialData: { total: 0, data: [] } });
|
|
|
+
|
|
|
+const model = shallowRef<TagQuery>();
|
|
|
+const searchFormProps = reactive<VxeFormProps<TagQuery>>({
|
|
|
+ titleWidth: 100,
|
|
|
+ titleAlign: 'right',
|
|
|
+ titleColon: true,
|
|
|
+ data: {
|
|
|
+ name: '',
|
|
|
+ status: void 0,
|
|
|
+ },
|
|
|
+ items: [
|
|
|
+ { field: 'name', title: '标签名称', span: 6, itemRender: { name: 'VxeInput' } },
|
|
|
+ {
|
|
|
+ field: 'status', title: '是否启用', span: 6, itemRender: {
|
|
|
+ name: 'VxeSelect',
|
|
|
+ options: statusOptions,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ { field: 'status', title: '创建者', span: 6, itemRender: { name: 'VxeInput' } },
|
|
|
+ {
|
|
|
+ field: 'parentId', title: '上级标签', span: 6, itemRender: {
|
|
|
+ name: 'VxeSelect',
|
|
|
+ props: {
|
|
|
+ loading: tagsLoading,
|
|
|
+ options: computed(() => tags.value.data),
|
|
|
+ optionProps: { value: 'id', label: 'name' },
|
|
|
+ clearable: true, multiple: true, filterable: true,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ span: 6, itemRender: {
|
|
|
+ name: 'VxeButtonGroup',
|
|
|
+ options: [
|
|
|
+ { type: 'submit', content: '查询', status: 'primary' },
|
|
|
+ { type: 'reset', content: '清空' },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+});
|
|
|
+const searchFormEmits: VxeFormListeners<TagQuery> = {
|
|
|
+ submit({ data }) { model.value = { ...data }; },
|
|
|
+ reset({ data }) { model.value = { ...data }; },
|
|
|
+};
|
|
|
+
|
|
|
+const gridRef = ref<VxeGridInstance<TagModel>>();
|
|
|
+const gridOptions = reactive<VxeGridProps<TagModel>>({
|
|
|
+ id: 'tag-list',
|
|
|
+ border: true,
|
|
|
+ showOverflow: true,
|
|
|
+ height: 'auto',
|
|
|
+ scrollY: { enabled: true, gt: 0 },
|
|
|
+ toolbarConfig: {
|
|
|
+ custom: true,
|
|
|
+ zoom: true,
|
|
|
+ slots: {
|
|
|
+ buttons: 'handle',
|
|
|
+ tools: 'toolbar-extra',
|
|
|
+ },
|
|
|
+ },
|
|
|
+ columnConfig: {
|
|
|
+ resizable: true,
|
|
|
+ },
|
|
|
+ customConfig: {
|
|
|
+ storage: true,
|
|
|
+ },
|
|
|
+ columns: [
|
|
|
+ { type: 'seq', width: 70, fixed: 'left' },
|
|
|
+ { field: 'name', title: '标签名称', minWidth: 160 },
|
|
|
+ { field: 'parentName', title: '上级标签', minWidth: 160 },
|
|
|
+ { field: 'update_time', title: '最近一次修改时间', minWidth: 160 },
|
|
|
+ { field: 'user_name', title: '创建者', minWidth: 160 },
|
|
|
+ {
|
|
|
+ field: 'status', title: '启用状态', minWidth: 100, cellRender: {
|
|
|
+ name: 'VxeSwitch',
|
|
|
+ props: {
|
|
|
+ openLabel: '启用', openValue: '0',
|
|
|
+ closeLabel: '停用', closeValue: '1',
|
|
|
+ },
|
|
|
+ events: {
|
|
|
+ change({ row, rowIndex }, { value }) {
|
|
|
+ row.status = { '1': '0', '0': '1' }[ value as string ] as any;
|
|
|
+ updateTagStatus(row, rowIndex, value);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ title: '操作',
|
|
|
+ width: 200,
|
|
|
+ cellRender: {
|
|
|
+ name: 'VxeButtonGroup',
|
|
|
+ props: {
|
|
|
+ mode: 'text',
|
|
|
+ },
|
|
|
+ options: [
|
|
|
+ { content: '修改', name: 'editTag' },
|
|
|
+ { content: '删除', status: 'error', name: 'deleteTag' },
|
|
|
+ ],
|
|
|
+ events: {
|
|
|
+ click({ row, rowIndex }, { name }) {
|
|
|
+ let method;
|
|
|
+ if ( name === 'editTag' ) { method = editTag; } else if ( name === 'deleteTag' ) { method = deleteTag; }
|
|
|
+ method?.(row, rowIndex);
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ data: [],
|
|
|
+});
|
|
|
+const gridEvents: VxeGridListeners = {};
|
|
|
+
|
|
|
+const { loading, page, pageSize, total, onSuccess, replace, refresh, remove } = usePagination(
|
|
|
+ (page, size) => tagsMethod(page, size, model.value), {
|
|
|
+ initialData: { data: [], total: 0 },
|
|
|
+ initialPage: 1,
|
|
|
+ initialPageSize: 100,
|
|
|
+ watchingStates: [ model ],
|
|
|
+ immediate: false,
|
|
|
+ },
|
|
|
+);
|
|
|
+onSuccess(({ data: { data } }) => {
|
|
|
+ gridRef.value?.loadData(data);
|
|
|
+});
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ model.value = toRaw(searchFormProps.data);
|
|
|
+});
|
|
|
+
|
|
|
+function updateTagStatus(model: TagModel, index: number, status: TagModel['status']) {
|
|
|
+ const { id, name } = model;
|
|
|
+ const label = { '1': '停用', '0': '启用' }[ status ];
|
|
|
+ VxeUI.modal.confirm({
|
|
|
+ title: `启用状态`,
|
|
|
+ content: `确认要 ${ label } ${ name } 标签吗?`,
|
|
|
+ showClose: false,
|
|
|
+ onConfirm() {
|
|
|
+ tagUpdateStatusMethod({ id, status }).then(() => {
|
|
|
+ notification.success({
|
|
|
+ message: `${ label }标签: ${ name }`,
|
|
|
+ description: '操作成功',
|
|
|
+ });
|
|
|
+ model.status = status;
|
|
|
+ replace(model, index);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function deleteTag(model: TagModel, index: number) {
|
|
|
+ const { name } = model;
|
|
|
+ VxeUI.modal.confirm({
|
|
|
+ title: `删除标签`,
|
|
|
+ content: `确认要删除 ${ name } 标签吗?`,
|
|
|
+ showClose: false,
|
|
|
+ onConfirm() {
|
|
|
+ tagDeleteMethod(model).then(() => {
|
|
|
+ notification.success({
|
|
|
+ message: `删除标签: ${ name }`,
|
|
|
+ description: '操作成功',
|
|
|
+ });
|
|
|
+ refresh(page.value);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function editTag(model?: TagModel, index?: number) {
|
|
|
+ VxeUI.modal.open({
|
|
|
+ title: model?.id ? `修改标签` : `新增标签`,
|
|
|
+ escClosable: true,
|
|
|
+ destroyOnClose: true,
|
|
|
+ id: `tag-edit-modal`,
|
|
|
+ remember: true,
|
|
|
+ storage: true,
|
|
|
+ slots: {
|
|
|
+ default() {
|
|
|
+ return h(TagEdit, <any> {
|
|
|
+ data: model, onSubmit(data: TagModel) {
|
|
|
+ refresh(page.value);
|
|
|
+ VxeUI.modal.close(`tag-edit-modal`);
|
|
|
+ },
|
|
|
+ });
|
|
|
+ },
|
|
|
+ },
|
|
|
+ });
|
|
|
+}
|
|
|
+</script>
|
|
|
+<template>
|
|
|
+ <div class="page-container flex flex-col">
|
|
|
+ <header class="flex-none">
|
|
|
+ <vxe-form v-bind="searchFormProps" v-on="searchFormEmits"></vxe-form>
|
|
|
+ </header>
|
|
|
+ <main class="flex-auto overflow-hidden">
|
|
|
+ <vxe-grid ref="gridRef" v-bind="gridOptions" v-on="gridEvents" :loading="loading">
|
|
|
+ <template #handle>
|
|
|
+ <vxe-button status="primary" @click="editTag()">新增</vxe-button>
|
|
|
+ </template>
|
|
|
+ <template #toolbar-extra>
|
|
|
+ <vxe-button style="margin-right: 12px;" icon="vxe-icon-repeat" circle @click="refresh(page)"></vxe-button>
|
|
|
+ </template>
|
|
|
+ </vxe-grid>
|
|
|
+ </main>
|
|
|
+ <footer class="flex-none">
|
|
|
+ <vxe-pager
|
|
|
+ v-model:current-page="page"
|
|
|
+ v-model:page-size="pageSize"
|
|
|
+ :total="total"
|
|
|
+ :layouts="['Home', 'PrevJump', 'PrevPage', 'Number', 'NextPage', 'NextJump', 'End', 'Sizes', 'FullJump', 'Total']"
|
|
|
+ />
|
|
|
+ </footer>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<style scoped lang="scss">
|
|
|
+.page-container {
|
|
|
+ padding: 0 24px;
|
|
|
+ max-height: var(--page-main-container);
|
|
|
+}
|
|
|
+</style>
|