mirror of
				https://github.com/Tencent/tmagic-editor.git
				synced 2025-11-04 10:49:51 +08:00 
			
		
		
		
	feat(admin): 使用@tmagic/table重构活动列表
This commit is contained in:
		
							parent
							
								
									032ec81b86
								
							
						
					
					
						commit
						8fa1d4a5c3
					
				
							
								
								
									
										66
									
								
								magic-admin/web/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										66
									
								
								magic-admin/web/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -14,14 +14,13 @@
 | 
			
		||||
        "@tmagic/form": "1.2.0",
 | 
			
		||||
        "@tmagic/schema": "1.2.0",
 | 
			
		||||
        "@tmagic/stage": "1.2.0",
 | 
			
		||||
        "@tmagic/table": "1.2.0",
 | 
			
		||||
        "@tmagic/utils": "1.2.0",
 | 
			
		||||
        "axios": "^0.27.2",
 | 
			
		||||
        "axios-jsonp": "^1.0.4",
 | 
			
		||||
        "core-js": "^3.20.0",
 | 
			
		||||
        "element-plus": "^2.2.19",
 | 
			
		||||
        "js-cookie": "^3.0.0",
 | 
			
		||||
        "moment": "^2.29.3",
 | 
			
		||||
        "moment-timezone": "^0.5.34",
 | 
			
		||||
        "serialize-javascript": "^6.0.0",
 | 
			
		||||
        "vue": "^3.2.26",
 | 
			
		||||
        "vue-router": "^4.0.3"
 | 
			
		||||
@ -3068,6 +3067,25 @@
 | 
			
		||||
        "node": ">=14"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@tmagic/table": {
 | 
			
		||||
      "version": "1.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@tmagic/table/-/table-1.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-sXUeR/wkjAeeFo1XU/v4LVbzWCSNJ5ZtzmSPuZvWbDTUEDvsHNuujoEizGngsL0h+qYkAt+ZkzFdV95M++Y1fA==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "@tmagic/design": "1.2.0",
 | 
			
		||||
        "@tmagic/form": "1.2.0",
 | 
			
		||||
        "@tmagic/utils": "1.2.0",
 | 
			
		||||
        "lodash-es": "^4.17.21",
 | 
			
		||||
        "vue": "^3.2.37"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": ">=14"
 | 
			
		||||
      },
 | 
			
		||||
      "peerDependencies": {
 | 
			
		||||
        "@tmagic/form": "1.2.0",
 | 
			
		||||
        "vue": "^3.2.37"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/@tmagic/utils": {
 | 
			
		||||
      "version": "1.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@tmagic/utils/-/utils-1.2.0.tgz",
 | 
			
		||||
@ -12273,25 +12291,6 @@
 | 
			
		||||
      "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/moment": {
 | 
			
		||||
      "version": "2.29.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
 | 
			
		||||
      "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw==",
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/moment-timezone": {
 | 
			
		||||
      "version": "0.5.34",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz",
 | 
			
		||||
      "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==",
 | 
			
		||||
      "dependencies": {
 | 
			
		||||
        "moment": ">= 2.9.0"
 | 
			
		||||
      },
 | 
			
		||||
      "engines": {
 | 
			
		||||
        "node": "*"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "node_modules/monaco-editor": {
 | 
			
		||||
      "version": "0.34.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.0.tgz",
 | 
			
		||||
@ -18982,6 +18981,18 @@
 | 
			
		||||
        "moveable-helper": "^0.4.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@tmagic/table": {
 | 
			
		||||
      "version": "1.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@tmagic/table/-/table-1.2.0.tgz",
 | 
			
		||||
      "integrity": "sha512-sXUeR/wkjAeeFo1XU/v4LVbzWCSNJ5ZtzmSPuZvWbDTUEDvsHNuujoEizGngsL0h+qYkAt+ZkzFdV95M++Y1fA==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "@tmagic/design": "1.2.0",
 | 
			
		||||
        "@tmagic/form": "1.2.0",
 | 
			
		||||
        "@tmagic/utils": "1.2.0",
 | 
			
		||||
        "lodash-es": "^4.17.21",
 | 
			
		||||
        "vue": "^3.2.37"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "@tmagic/utils": {
 | 
			
		||||
      "version": "1.2.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/@tmagic/utils/-/utils-1.2.0.tgz",
 | 
			
		||||
@ -26062,19 +26073,6 @@
 | 
			
		||||
      "integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "moment": {
 | 
			
		||||
      "version": "2.29.3",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.3.tgz",
 | 
			
		||||
      "integrity": "sha512-c6YRvhEo//6T2Jz/vVtYzqBzwvPT95JBQ+smCytzf7c50oMZRsR/a4w88aD34I+/QVSfnoAnSBFPJHItlOMJVw=="
 | 
			
		||||
    },
 | 
			
		||||
    "moment-timezone": {
 | 
			
		||||
      "version": "0.5.34",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.34.tgz",
 | 
			
		||||
      "integrity": "sha512-3zAEHh2hKUs3EXLESx/wsgw6IQdusOT8Bxm3D9UrHPQR7zlMmzwybC8zHEM1tQ4LJwP7fcxrWr8tuBg05fFCbg==",
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "moment": ">= 2.9.0"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "monaco-editor": {
 | 
			
		||||
      "version": "0.34.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.0.tgz",
 | 
			
		||||
 | 
			
		||||
@ -15,14 +15,13 @@
 | 
			
		||||
    "@tmagic/form": "1.2.0",
 | 
			
		||||
    "@tmagic/schema": "1.2.0",
 | 
			
		||||
    "@tmagic/stage": "1.2.0",
 | 
			
		||||
    "@tmagic/table": "1.2.0",
 | 
			
		||||
    "@tmagic/utils": "1.2.0",
 | 
			
		||||
    "axios": "^0.27.2",
 | 
			
		||||
    "axios-jsonp": "^1.0.4",
 | 
			
		||||
    "core-js": "^3.20.0",
 | 
			
		||||
    "element-plus": "^2.2.19",
 | 
			
		||||
    "js-cookie": "^3.0.0",
 | 
			
		||||
    "moment": "^2.29.3",
 | 
			
		||||
    "moment-timezone": "^0.5.34",
 | 
			
		||||
    "serialize-javascript": "^6.0.0",
 | 
			
		||||
    "vue": "^3.2.26",
 | 
			
		||||
    "vue-router": "^4.0.3"
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,14 @@
 | 
			
		||||
        </el-row>
 | 
			
		||||
      </template>
 | 
			
		||||
 | 
			
		||||
      <m-table :data="tableData.res" :config="columns" @sort-change="sortChange" />
 | 
			
		||||
      <magic-table
 | 
			
		||||
        rowkey-name="actId"
 | 
			
		||||
        :data="tableData.res.data"
 | 
			
		||||
        :loading="!tableData.res.fetch"
 | 
			
		||||
        :empty-text="tableData.res.errorMsg"
 | 
			
		||||
        :columns="columns"
 | 
			
		||||
        @sort-change="sortChange"
 | 
			
		||||
      />
 | 
			
		||||
 | 
			
		||||
      <div class="bottom clearfix" style="margin-top: 10px; text-align: right">
 | 
			
		||||
        <el-pagination
 | 
			
		||||
@ -57,15 +64,13 @@
 | 
			
		||||
      </div>
 | 
			
		||||
    </el-card>
 | 
			
		||||
 | 
			
		||||
    <form-dialog
 | 
			
		||||
    <MFormDialog
 | 
			
		||||
      ref="dialog"
 | 
			
		||||
      title="新建活动"
 | 
			
		||||
      :visible="formDialogVisible"
 | 
			
		||||
      :values="actValues"
 | 
			
		||||
      :action="action"
 | 
			
		||||
      :config="formConfig"
 | 
			
		||||
      @afterAction="afterAction"
 | 
			
		||||
      @close="closeFormDialogHandler"
 | 
			
		||||
    ></form-dialog>
 | 
			
		||||
      @submit="submitHandler"
 | 
			
		||||
    ></MFormDialog>
 | 
			
		||||
  </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@ -76,22 +81,25 @@ import { defineComponent, onMounted, watch } from '@vue/runtime-core';
 | 
			
		||||
import { ElMessage } from 'element-plus';
 | 
			
		||||
import Cookies from 'js-cookie';
 | 
			
		||||
 | 
			
		||||
import actApi, { ActListItem, ActListQuery, ActListRes, CopyInfo, OrderItem } from '@src/api/act';
 | 
			
		||||
import FormDialog from '@src/components/form-dialog.vue';
 | 
			
		||||
import MTable from '@src/components/table.vue';
 | 
			
		||||
import { MFormDialog } from '@tmagic/form';
 | 
			
		||||
import { MagicTable } from '@tmagic/table';
 | 
			
		||||
 | 
			
		||||
import actApi, { ActInfoDetail, ActListItem, ActListQuery, ActListRes, CopyInfo, OrderItem } from '@src/api/act';
 | 
			
		||||
import { getActListFormConfig } from '@src/config/act-list-config';
 | 
			
		||||
import { BlankActFormConfig } from '@src/config/blank-act-config';
 | 
			
		||||
import { ActStatus } from '@src/config/status';
 | 
			
		||||
import type { ActFormValue, ColumnItem } from '@src/typings';
 | 
			
		||||
import type { ActFormValue } from '@src/typings';
 | 
			
		||||
import { status } from '@src/use/use-status';
 | 
			
		||||
import { Res } from '@src/util/request';
 | 
			
		||||
import { datetimeFormatter } from '@src/util/utils';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: 'act-list',
 | 
			
		||||
  components: { FormDialog, MTable },
 | 
			
		||||
  components: { MFormDialog, MagicTable },
 | 
			
		||||
 | 
			
		||||
  setup() {
 | 
			
		||||
    const dialog = ref<InstanceType<typeof MFormDialog>>();
 | 
			
		||||
 | 
			
		||||
    const actStatus = [...status.actStatus];
 | 
			
		||||
    const pageStatus = [...status.pageStatus];
 | 
			
		||||
    const router = useRouter();
 | 
			
		||||
@ -119,7 +127,6 @@ export default defineComponent({
 | 
			
		||||
    const tableData = reactive<{ res: ActListRes }>({
 | 
			
		||||
      res: { data: [], fetch: false, errorMsg: '', total: 0 },
 | 
			
		||||
    });
 | 
			
		||||
    const formDialogVisible = ref<boolean>(false);
 | 
			
		||||
    // 更新活动列表
 | 
			
		||||
    const getActs = async () => {
 | 
			
		||||
      const res = await actApi.getList({
 | 
			
		||||
@ -165,7 +172,7 @@ export default defineComponent({
 | 
			
		||||
    const copyActAfterHandler = async () => {
 | 
			
		||||
      await getActs();
 | 
			
		||||
    };
 | 
			
		||||
    const columns: ColumnItem[] = getActListFormConfig(
 | 
			
		||||
    const columns = getActListFormConfig(
 | 
			
		||||
      pageStatusFormatter,
 | 
			
		||||
      actStatusFormatter,
 | 
			
		||||
      router,
 | 
			
		||||
@ -240,7 +247,7 @@ export default defineComponent({
 | 
			
		||||
        actBeginTime: datetimeFormatter(new Date()),
 | 
			
		||||
        actEndTime: datetimeFormatter(new Date()),
 | 
			
		||||
      };
 | 
			
		||||
      formDialogVisible.value = true;
 | 
			
		||||
      dialog.value && (dialog.value.dialogVisible = true);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const afterAction = (res: Res<{ actId: number }>) => {
 | 
			
		||||
@ -248,28 +255,30 @@ export default defineComponent({
 | 
			
		||||
      router.push(`/editor/${actId}`);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    const closeFormDialogHandler = () => {
 | 
			
		||||
      formDialogVisible.value = false;
 | 
			
		||||
    const submitHandler = async (info: ActInfoDetail) => {
 | 
			
		||||
      const res = await actApi.saveAct({
 | 
			
		||||
        data: info,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      afterAction(res);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      dialog,
 | 
			
		||||
      actStatus,
 | 
			
		||||
      columns,
 | 
			
		||||
      query,
 | 
			
		||||
      tableData,
 | 
			
		||||
      actValues: toRefs(actValues).data,
 | 
			
		||||
      formDialogVisible,
 | 
			
		||||
      formConfig: BlankActFormConfig,
 | 
			
		||||
      action: actApi.saveAct,
 | 
			
		||||
      searchChangeHandler,
 | 
			
		||||
      actStatusChangeHandle,
 | 
			
		||||
      pageTitleChangeHandler,
 | 
			
		||||
      sortChange,
 | 
			
		||||
      handleSizeChange,
 | 
			
		||||
      handleCurrentChange,
 | 
			
		||||
      afterAction,
 | 
			
		||||
      closeFormDialogHandler,
 | 
			
		||||
      newHandler,
 | 
			
		||||
      submitHandler,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@ -1,144 +0,0 @@
 | 
			
		||||
<!-- 新建活动对话框 -->
 | 
			
		||||
<template>
 | 
			
		||||
  <el-dialog
 | 
			
		||||
    custom-class="m-dialog"
 | 
			
		||||
    top="10%"
 | 
			
		||||
    :title="title"
 | 
			
		||||
    :model-value="dialogVisible"
 | 
			
		||||
    :appendToBody="true"
 | 
			
		||||
    :close-on-click-modal="false"
 | 
			
		||||
    :before-close="closeHandler"
 | 
			
		||||
  >
 | 
			
		||||
    <div class="m-dialog-body" :style="`max-height: ${bodyHeight}; overflow-y: auto; overflow-x: hidden;`">
 | 
			
		||||
      <m-form v-if="dialogVisible" ref="form" :config="config" :init-values="formInitValues"></m-form>
 | 
			
		||||
      <slot></slot>
 | 
			
		||||
    </div>
 | 
			
		||||
 | 
			
		||||
    <template #footer>
 | 
			
		||||
      <el-row class="dialog-footer">
 | 
			
		||||
        <el-col :span="12" style="text-align: left">
 | 
			
		||||
          <div style="min-height: 1px">
 | 
			
		||||
            <slot name="left"></slot>
 | 
			
		||||
          </div>
 | 
			
		||||
        </el-col>
 | 
			
		||||
        <el-col :span="12">
 | 
			
		||||
          <slot name="footer">
 | 
			
		||||
            <el-button @click="$emit('close')" size="small">取 消</el-button>
 | 
			
		||||
            <el-button type="primary" size="small" :loading="saveFetch" @click="save">确定</el-button>
 | 
			
		||||
          </slot>
 | 
			
		||||
        </el-col>
 | 
			
		||||
      </el-row>
 | 
			
		||||
    </template>
 | 
			
		||||
  </el-dialog>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, PropType, ref } from 'vue';
 | 
			
		||||
import { ElMessage } from 'element-plus';
 | 
			
		||||
 | 
			
		||||
import { MForm } from '@tmagic/form';
 | 
			
		||||
 | 
			
		||||
import type { ActFormValue, FormConfigItem } from '@src/typings';
 | 
			
		||||
import { Res } from '@src/util/request';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: 'm-form-dialog',
 | 
			
		||||
  props: {
 | 
			
		||||
    values: {
 | 
			
		||||
      type: Object as PropType<ActFormValue>,
 | 
			
		||||
      default: () => ({}),
 | 
			
		||||
    },
 | 
			
		||||
    title: String,
 | 
			
		||||
    config: {
 | 
			
		||||
      type: Array as PropType<FormConfigItem[]>,
 | 
			
		||||
      default: () => [],
 | 
			
		||||
    },
 | 
			
		||||
    visible: {
 | 
			
		||||
      type: Boolean,
 | 
			
		||||
      default: () => false,
 | 
			
		||||
    },
 | 
			
		||||
    action: {
 | 
			
		||||
      type: Function as PropType<(options: { data: any }) => Res>,
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  emits: ['afterAction', 'close'],
 | 
			
		||||
 | 
			
		||||
  setup(props, { emit }) {
 | 
			
		||||
    const form = ref<InstanceType<typeof MForm>>();
 | 
			
		||||
    const saveFetch = ref(false);
 | 
			
		||||
    const dialogVisible = computed(() => props.visible);
 | 
			
		||||
    const formInitValues = computed(() => props.values);
 | 
			
		||||
    // 关闭对话框
 | 
			
		||||
    const closeHandler = () => {
 | 
			
		||||
      emit('close');
 | 
			
		||||
      form.value?.resetForm();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    // 保存活动
 | 
			
		||||
    const save = async () => {
 | 
			
		||||
      if (saveFetch.value) {
 | 
			
		||||
        return;
 | 
			
		||||
      }
 | 
			
		||||
      saveFetch.value = true;
 | 
			
		||||
      try {
 | 
			
		||||
        const values = await form.value?.submitForm();
 | 
			
		||||
        if (!values) {
 | 
			
		||||
          emit('close');
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
        const res = await props.action?.({ data: values });
 | 
			
		||||
        if (res) {
 | 
			
		||||
          if (res.ret === 0) {
 | 
			
		||||
            ElMessage.success(res.msg || '保存成功');
 | 
			
		||||
            emit('close');
 | 
			
		||||
            emit('afterAction', res);
 | 
			
		||||
          } else {
 | 
			
		||||
            ElMessage({
 | 
			
		||||
              type: 'error',
 | 
			
		||||
              duration: 10000,
 | 
			
		||||
              showClose: true,
 | 
			
		||||
              dangerouslyUseHTMLString: true,
 | 
			
		||||
              message: res.msg || '保存失败',
 | 
			
		||||
            });
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          emit('close');
 | 
			
		||||
        }
 | 
			
		||||
      } catch (e) {
 | 
			
		||||
        ElMessage({
 | 
			
		||||
          type: 'error',
 | 
			
		||||
          duration: 10000,
 | 
			
		||||
          showClose: true,
 | 
			
		||||
          message: (e as Error).message,
 | 
			
		||||
          dangerouslyUseHTMLString: true,
 | 
			
		||||
        });
 | 
			
		||||
      }
 | 
			
		||||
      saveFetch.value = false;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      dialogVisible,
 | 
			
		||||
      saveFetch,
 | 
			
		||||
      form,
 | 
			
		||||
      formInitValues,
 | 
			
		||||
      bodyHeight: ref(`${document.body.clientHeight - 194}px`),
 | 
			
		||||
      closeHandler,
 | 
			
		||||
      save,
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style>
 | 
			
		||||
.m-dialog .el-dialog__body {
 | 
			
		||||
  padding: 0 !important;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.m-dialog .m-dialog-body {
 | 
			
		||||
  padding: 0 20px;
 | 
			
		||||
}
 | 
			
		||||
.el-table .m-form-item .el-form-item {
 | 
			
		||||
  margin-bottom: 0;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -1,155 +0,0 @@
 | 
			
		||||
<!-- 表格组件 -->
 | 
			
		||||
<template>
 | 
			
		||||
  <el-table
 | 
			
		||||
    :data="tableData?.data"
 | 
			
		||||
    :empty-text="tableData?.errorMsg || '暂无数据'"
 | 
			
		||||
    @sort-change="sortChange"
 | 
			
		||||
    v-loading="!tableData?.fetch"
 | 
			
		||||
    border
 | 
			
		||||
  >
 | 
			
		||||
    <!-- 解析表格配置 -->
 | 
			
		||||
    <template v-for="(item, columnIndex) in columns">
 | 
			
		||||
      <!-- 操作栏 -->
 | 
			
		||||
      <el-table-column
 | 
			
		||||
        :key="columnIndex + '1'"
 | 
			
		||||
        v-if="item.actions"
 | 
			
		||||
        :prop="item.prop"
 | 
			
		||||
        :label="item.label"
 | 
			
		||||
        :width="item.width"
 | 
			
		||||
        :fixed="item.fixed"
 | 
			
		||||
      >
 | 
			
		||||
        <template #default="{ row, $index }">
 | 
			
		||||
          <el-button
 | 
			
		||||
            class="action-btn"
 | 
			
		||||
            v-for="(action, actionIndex) in item.actions"
 | 
			
		||||
            :key="actionIndex"
 | 
			
		||||
            @click="actionHandler(action, row, $index)"
 | 
			
		||||
            text
 | 
			
		||||
            size="small"
 | 
			
		||||
            v-html="action.text"
 | 
			
		||||
          ></el-button>
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-table-column>
 | 
			
		||||
      <!-- 数据展示栏 -->
 | 
			
		||||
      <el-table-column
 | 
			
		||||
        v-else
 | 
			
		||||
        :key="columnIndex + '2'"
 | 
			
		||||
        :prop="item.prop"
 | 
			
		||||
        :label="item.label"
 | 
			
		||||
        :width="item.width"
 | 
			
		||||
        :fixed="item.fixed"
 | 
			
		||||
        :sortable="item.sortable ? item.sortable : false"
 | 
			
		||||
        show-overflow-tooltip
 | 
			
		||||
        :type="item.type"
 | 
			
		||||
      >
 | 
			
		||||
        <template #default="{ row }">
 | 
			
		||||
          <!-- 展示为文字链接 -->
 | 
			
		||||
          <el-button v-if="item.action === 'actionLink'" text @click="item.handler(row)">
 | 
			
		||||
            {{ formatter(item, row) }}
 | 
			
		||||
          </el-button>
 | 
			
		||||
          <!-- 展示为标签 -->
 | 
			
		||||
          <el-tag v-else-if="item.action === 'tag'" :type="statusTagType[row[item.prop]]" close-transition>
 | 
			
		||||
            {{ formatter(item, row) }}
 | 
			
		||||
          </el-tag>
 | 
			
		||||
          <!-- 扩展表格(子表) -->
 | 
			
		||||
          <el-table
 | 
			
		||||
            v-else-if="item.table"
 | 
			
		||||
            :data="row.pages"
 | 
			
		||||
            empty-text="暂无数据"
 | 
			
		||||
            border
 | 
			
		||||
            size="small"
 | 
			
		||||
            class="sub-table"
 | 
			
		||||
          >
 | 
			
		||||
            <!-- 解析子表 -->
 | 
			
		||||
            <el-table-column
 | 
			
		||||
              v-for="(column, columnIndex) in item.table"
 | 
			
		||||
              :key="columnIndex"
 | 
			
		||||
              :prop="column.prop"
 | 
			
		||||
              :label="column.label"
 | 
			
		||||
            >
 | 
			
		||||
              <template #default="page">
 | 
			
		||||
                {{ formatter(column, page.row) }}
 | 
			
		||||
              </template>
 | 
			
		||||
            </el-table-column>
 | 
			
		||||
          </el-table>
 | 
			
		||||
          <div v-else v-html="formatter(item, row)"></div>
 | 
			
		||||
        </template>
 | 
			
		||||
      </el-table-column>
 | 
			
		||||
    </template>
 | 
			
		||||
  </el-table>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import { computed, defineComponent, PropType } from 'vue';
 | 
			
		||||
 | 
			
		||||
import { ActListItem, ActListRes } from '@src/api/act';
 | 
			
		||||
import type { ActionItem, ColumnItem } from '@src/typings';
 | 
			
		||||
import { status } from '@src/use/use-status';
 | 
			
		||||
 | 
			
		||||
export default defineComponent({
 | 
			
		||||
  name: 'm-table',
 | 
			
		||||
 | 
			
		||||
  props: {
 | 
			
		||||
    data: {
 | 
			
		||||
      type: Object as PropType<ActListRes>,
 | 
			
		||||
      default: () => ({ data: [], fetch: true, errorMsg: '', total: 0 }),
 | 
			
		||||
    },
 | 
			
		||||
    config: {
 | 
			
		||||
      type: Array,
 | 
			
		||||
      default: () => [],
 | 
			
		||||
    },
 | 
			
		||||
  },
 | 
			
		||||
 | 
			
		||||
  emits: ['sort-change'],
 | 
			
		||||
 | 
			
		||||
  setup(props, { emit }) {
 | 
			
		||||
    const tableData = computed(() => props.data);
 | 
			
		||||
    const columns = computed(() => props.config);
 | 
			
		||||
    const isValidProp = (row: object, prop: string) => prop && prop in row;
 | 
			
		||||
    return {
 | 
			
		||||
      tableData,
 | 
			
		||||
      columns,
 | 
			
		||||
      statusTagType: [...status.statusTagType],
 | 
			
		||||
 | 
			
		||||
      // 统一处理表格项操作
 | 
			
		||||
      actionHandler: async (action: ActionItem, row: ActListItem) => {
 | 
			
		||||
        await action.handler?.(row);
 | 
			
		||||
        action.after?.();
 | 
			
		||||
      },
 | 
			
		||||
      // 展示数据格式化
 | 
			
		||||
      formatter: (item: ColumnItem, row: ActListItem) => {
 | 
			
		||||
        if (!isValidProp(row, item.prop)) {
 | 
			
		||||
          return '';
 | 
			
		||||
        }
 | 
			
		||||
        if (item.formatter) {
 | 
			
		||||
          try {
 | 
			
		||||
            return item.formatter(row[item.prop], row);
 | 
			
		||||
          } catch (e) {
 | 
			
		||||
            console.log((e as Error).message);
 | 
			
		||||
            return row[item.prop];
 | 
			
		||||
          }
 | 
			
		||||
        } else {
 | 
			
		||||
          return row[item.prop];
 | 
			
		||||
        }
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      // 排序
 | 
			
		||||
      sortChange: (column: { prop: string; order: string }) => {
 | 
			
		||||
        emit('sort-change', column);
 | 
			
		||||
      },
 | 
			
		||||
    };
 | 
			
		||||
  },
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="scss">
 | 
			
		||||
.sub-table {
 | 
			
		||||
  margin-top: 10px;
 | 
			
		||||
  margin-bottom: 10px;
 | 
			
		||||
 | 
			
		||||
  th {
 | 
			
		||||
    background-color: #ffffff;
 | 
			
		||||
    color: #909399;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
@ -18,6 +18,8 @@
 | 
			
		||||
 | 
			
		||||
import type { Router } from 'vue-router';
 | 
			
		||||
 | 
			
		||||
import { MagicTable } from '@tmagic/table';
 | 
			
		||||
 | 
			
		||||
import type { ActListItem } from '@src/api/act';
 | 
			
		||||
import type { ColumnItem } from '@src/typings';
 | 
			
		||||
import { datetimeFormatter } from '@src/util/utils';
 | 
			
		||||
@ -29,31 +31,38 @@ export const getActListFormConfig = (
 | 
			
		||||
  router: Router,
 | 
			
		||||
  copyActHandler: ColumnItem['handler'],
 | 
			
		||||
  copyActAfterHandler: ColumnItem['handler'],
 | 
			
		||||
): ColumnItem[] => [
 | 
			
		||||
) => [
 | 
			
		||||
  {
 | 
			
		||||
    prop: '',
 | 
			
		||||
    type: 'expand',
 | 
			
		||||
    table: [
 | 
			
		||||
      {
 | 
			
		||||
        prop: 'pageTitle',
 | 
			
		||||
        label: '页面标题',
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        prop: 'pagePublishTime',
 | 
			
		||||
        label: '页面发布时间',
 | 
			
		||||
        formatter: datetimeFormatter,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        prop: 'pagePublishStatus',
 | 
			
		||||
        label: '页面状态',
 | 
			
		||||
        formatter: pageStatusFormatter,
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        prop: 'pagePublishOperator',
 | 
			
		||||
        label: '发布人',
 | 
			
		||||
        formatter: (v: string | number | Date) => (v as string) || '-',
 | 
			
		||||
      },
 | 
			
		||||
    ],
 | 
			
		||||
    component: MagicTable,
 | 
			
		||||
    props: (row: ActListItem) => ({
 | 
			
		||||
      data: row.pages,
 | 
			
		||||
      border: true,
 | 
			
		||||
      columns: [
 | 
			
		||||
        {
 | 
			
		||||
          prop: 'pageTitle',
 | 
			
		||||
          label: '页面标题',
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          prop: 'pagePublishTime',
 | 
			
		||||
          label: '页面发布时间',
 | 
			
		||||
          formatter: datetimeFormatter,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          prop: 'pagePublishStatus',
 | 
			
		||||
          label: '页面状态',
 | 
			
		||||
          action: 'tag',
 | 
			
		||||
          type: (v: number) => ['', 'success'][v],
 | 
			
		||||
          formatter: pageStatusFormatter,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          prop: 'pagePublishOperator',
 | 
			
		||||
          label: '发布人',
 | 
			
		||||
          formatter: (v: string | number | Date) => (v as string) || '-',
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    }),
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
    prop: 'actId',
 | 
			
		||||
@ -86,6 +95,7 @@ export const getActListFormConfig = (
 | 
			
		||||
    prop: 'actStatus',
 | 
			
		||||
    label: '活动状态',
 | 
			
		||||
    action: 'tag',
 | 
			
		||||
    type: (v: number) => ['info', '', 'success'][v],
 | 
			
		||||
    formatter: actStatusFormatter,
 | 
			
		||||
  },
 | 
			
		||||
  {
 | 
			
		||||
 | 
			
		||||
@ -19,9 +19,9 @@
 | 
			
		||||
import { ElMessage, ElMessageBox } from 'element-plus';
 | 
			
		||||
 | 
			
		||||
import type { MNode } from '@tmagic/schema';
 | 
			
		||||
import { isPage } from '@tmagic/utils';
 | 
			
		||||
 | 
			
		||||
import actApi from '@src/api/act';
 | 
			
		||||
import { isPage } from '@tmagic/utils';
 | 
			
		||||
 | 
			
		||||
export default {
 | 
			
		||||
  /**
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										4
									
								
								magic-admin/web/src/typings/index.d.ts
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								magic-admin/web/src/typings/index.d.ts
									
									
									
									
										vendored
									
									
								
							@ -134,8 +134,8 @@ export interface EditorInfo {
 | 
			
		||||
// 新建活动的初始值类型
 | 
			
		||||
export interface ActFormValue {
 | 
			
		||||
  operator: string;
 | 
			
		||||
  actBeginTime: string;
 | 
			
		||||
  actEndTime: string;
 | 
			
		||||
  actBeginTime: string | number;
 | 
			
		||||
  actEndTime: string | number;
 | 
			
		||||
}
 | 
			
		||||
// 侧边栏配置
 | 
			
		||||
export interface AsideState {
 | 
			
		||||
 | 
			
		||||
@ -16,25 +16,11 @@
 | 
			
		||||
 * limitations under the License.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
import momentTimezone from 'moment-timezone';
 | 
			
		||||
import serialize from 'serialize-javascript';
 | 
			
		||||
 | 
			
		||||
import { EditorInfo } from '@src/typings';
 | 
			
		||||
 | 
			
		||||
export const datetimeFormatter = function (v: string | number | Date): string {
 | 
			
		||||
  if (v) {
 | 
			
		||||
    let time = null;
 | 
			
		||||
    time = momentHandler(v);
 | 
			
		||||
    // 格式化为北京时间
 | 
			
		||||
    if (time !== 'Invalid date') {
 | 
			
		||||
      return time;
 | 
			
		||||
    }
 | 
			
		||||
    return '-';
 | 
			
		||||
  }
 | 
			
		||||
  return '-';
 | 
			
		||||
};
 | 
			
		||||
const momentHandler = (v: string | number | Date) =>
 | 
			
		||||
  momentTimezone.tz(v, 'Asia/Shanghai').format('YYYY-MM-DD HH:mm:ss');
 | 
			
		||||
export { datetimeFormatter } from '@tmagic/utils';
 | 
			
		||||
 | 
			
		||||
export const serializeConfig = function (value: EditorInfo): string {
 | 
			
		||||
  return serialize(value, {
 | 
			
		||||
 | 
			
		||||
@ -236,7 +236,7 @@ describe('List', () => {
 | 
			
		||||
    const wrapper = getWrapper();
 | 
			
		||||
    const createButton = wrapper.find('#create');
 | 
			
		||||
    await createButton.trigger('click');
 | 
			
		||||
    expect(wrapper.vm.formDialogVisible).toBe(true);
 | 
			
		||||
    expect(wrapper.vm.dialog?.dialogVisible).toBe(true);
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('路由的活动状态值缺省', () => {
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user