【Vue】ABP框架客户端开发

系列文章

【Vue】vue增加导航标签

本文链接:https://blog.csdn.net/youcheng_ge/article/details/134965353

【Vue】Element开发笔记

本文链接:https://blog.csdn.net/youcheng_ge/article/details/133947977

【Vue】vue,在Windows IIS平台部署

本文链接:https://blog.csdn.net/youcheng_ge/article/details/133859117

【Vue】vue2与WebApi跨域CORS问题

本文链接:https://blog.csdn.net/youcheng_ge/article/details/133808959

【Vue】nvm安装教程(解决npm下依赖包版本冲突)

本文链接:https://blog.csdn.net/youcheng_ge/article/details/132896207

【Vue】vue开发环境搭建教程(详细)

本文链接:https://blog.csdn.net/youcheng_ge/article/details/132689006

【Vue】日期格式化(全局)

本文链接:https://blog.csdn.net/youcheng_ge/article/details/135017332

【Vue】elementUI表格,导出Excel

本文链接:https://blog.csdn.net/youcheng_ge/article/details/135018489

【Vue】el-date-picker日期范围组件(本周、本月、上周)

本文链接:https://blog.csdn.net/youcheng_ge/article/details/135088143

【前端】前后端通信方法与差异

本文链接:https://blog.csdn.net/youcheng_ge/article/details/135153985


文章目录


前言

本专栏为 前端【Vue】专栏,主要介绍Vue知识点。对于刚进入计算机世界的学生来说,学习课本上的知识是远远不够的,并且国内教材偏旧,所以需要我们通过互联网自主学习。

这里普及一个知识:HTML已不仅仅只能开发 Web,也可以开发移动端(AndroidiOS),所以本专栏也会介绍 移动端的开发。

我个人将移动端开发,分为两大方向:

①原生开发

最早一批,使用安卓开发工具包(Android SDK)和Java语言来开发App的方式。原生开发允许开发者充分利用安卓平台的功能和特性,以及庞大的安卓开发社区资源。但缺点就是门槛高、需要适配不同尺寸的屏幕、测试繁琐,对开发人员技术要求高。
②混合开发(加壳方式)

当前热门,使用Web技术(网页三剑客HTML、CSS和JavaScript)开发App的方式,使用 vue.jsnode.jsAngular.jsReact.jsapi.js等框架开发。混合开发具有较高的开发效率和跨平台的优势,由于使用Web技术进行 界面渲染样式丰富、屏幕适配(栅格技术自适应)效果好。但缺点就是对底层硬件调用库尚不完善,有时候会发生异常,对框架依赖较高,不过库在不断完善中,主要的相机、相册、GPS、存储调用是没有问题的。

Vue是前端开发中的一个分支,它基于标准 HTML、CSS 和 JavaScript 构建,学习Vue不可以速成,得先熟悉网页三剑客(HTML、CSS和JavaScript)。Vue是一套声明式的、组件化的编程模型,帮助你高效地开发用户界面。

一、技术介绍

切换到根目录

bash 复制代码
cd mms_client

项目初始化

bash 复制代码
pnpm install

项目启动项

bash 复制代码
npm run dev:antd

二、项目源码

2.1 api功能方法

文件名: index.ts

路径: E:\MyProject\mms\mms_client\apps\web-antd\src\api\basic-data\index.ts

代码:

ts 复制代码
export * from './tax-0001';
export * from './tax-0003';
export * from './tax-0004';
export * from './tax-0006';
export * from './tax-0007';
export * from './tax-0008';
export * from './tax-0009';
export * from './tax-0010';
export * from './tax-0012';
export * from './tax-0020';
export * from './tax-0400';
export * from './tax-0410';
export * from './tax-2036';
export * from './tax-2037';
export * from './tax-0140';
export * from './tpa-1020';
export * from './tqa-0101';
export * from './tqa-0110';
export * from './tqa-1102';
export * from './ts-position';

文件名: tax-2036.ts

路径: E:\MyProject\mms\mms_client\apps\web-antd\src\api\basic-data\tax-2036.ts

代码:

ts 复制代码
import type { Recordable } from '@vben/types';

import { requestClient } from '#/api/request';

export namespace Tax2036Api {
  export interface Tax2036 {
    [key: string]: any;
    id: number;
    cPlantId: string;
    cStoreHouse: string;
    cStoLocation: string;
    cStoType: string;
    cTlDj?: null | string;
    cStoLocationTxt?: null | string;
    cRemark?: null | string;
    cIsUse: string;
    creationTime: string;
    lastModificationTime?: string;
  }

  export interface CreateUpdateTax2036Dto {
    cPlantId: string;
    cStoreHouse: string;
    cStoLocation: string;
    cStoType: string;
    cTlDj?: null | string;
    cStoLocationTxt?: null | string;
    cRemark?: null | string;
    cIsUse: string;
  }

  export interface PagedResultDto<T> {
    items: T[];
    totalCount: number;
  }
}

const apiUrl = '/api/app/t-aX_2036';

async function getTax2036List(params: Recordable<any>) {
  return requestClient.get<Tax2036Api.PagedResultDto<Tax2036Api.Tax2036>>(
    apiUrl,
    {
      params,
    },
  );
}

async function getTax2036ById(id: number) {
  return requestClient.get<Tax2036Api.Tax2036>(`${apiUrl}/${id}`);
}

async function createTax2036(data: Tax2036Api.CreateUpdateTax2036Dto) {
  return requestClient.post<Tax2036Api.Tax2036>(apiUrl, data);
}

async function updateTax2036(
  id: number,
  data: Tax2036Api.CreateUpdateTax2036Dto,
) {
  return requestClient.put<Tax2036Api.Tax2036>(`${apiUrl}/${id}`, data);
}

async function deleteTax2036(id: number) {
  return requestClient.delete(`${apiUrl}/${id}`);
}

export {
  createTax2036,
  deleteTax2036,
  getTax2036ById,
  getTax2036List,
  updateTax2036,
};

2.2 langs多语言(语言翻译)

文件名: mes.json

路径(英文):

E:\MyProject\mms\mms_client\apps\web-antd\src\locales\langs\en-US\mes.json

代码:

json 复制代码
    "tax2036": {
      "title": "Storage Location Master Data",
      "list": "Storage Location Master Data List",
      "name": "Storage Location",
      "operation": "Operation",
      "loading": "Loading {0} ...",
      "isUsing": "Include Disabled Data",
      "cPlantId": "Plant",
      "cStoreHouse": "Store House Code",
      "cStoLocation": "Storage Location",
      "cStoType": "Storage Type",
      "cTlDj": "Feeding Consumption Freeze Flag",
      "cStoLocationTxt": "Storage Location Description",
      "cRemark": "Remark",
      "cIsUse": "Available"
    },

路径(中文):

E:\MyProject\mms\mms_client\apps\web-antd\src\locales\langs\zh-CN\mes.json

代码:

json 复制代码
    "tax2036": {
      "title": "仓位基础数据维护",
      "list": "仓位基础数据维护列表",
      "name": "仓位",
      "operation": "操作",
      "loading": "正在加载 {0} ...",
      "isUsing": "查询禁用数据",
      "cPlantId": "实体工厂",
      "cStoreHouse": "实体库代码",
      "cStoLocation": "仓位",
      "cStoType": "仓储类型",
      "cTlDj": "投料耗用冻结标记",
      "cStoLocationTxt": "仓位描述",
      "cRemark": "备注",
      "cIsUse": "是否启用"
    },

2.3 router路由(菜单)

**文件名:**mes.ts

路径: E:\MyProject\mms\mms_client\apps\web-antd\src\router\routes\modules\mes.ts

代码

ts 复制代码
    {
    component: () =>
      import('#/views/mes/basic-data/tax-2036/list.vue'),
    meta: {
      icon: 'lucide:map-pin',
      title: $t('mes.basicData.tax2036.title'),
    },
    name: 'Tax2036Management',
    path: '/mes/basic-data/tax-2036',
    },

2.4 views视图(前端页面)

文件名: form.vue

路径: E:\MyProject\mms\mms_client\apps\web-antd\src\views\mes\basic-data\tax-2036\modules\form.vue

代码

vue 复制代码
<script lang="ts" setup>
import type { Tax2036Api } from '#/api';

import { computed, ref } from 'vue';

import { useVbenModal } from '@vben/common-ui';

import { Button } from 'ant-design-vue';

import { useVbenForm } from '#/adapter/form';
import { createTax2036, updateTax2036 } from '#/api';
import { $t } from '#/locales';

import { useFormSchema } from '../data';

const emit = defineEmits(['success']);
const formData = ref<Tax2036Api.Tax2036>();

const defaultValues: Tax2036Api.CreateUpdateTax2036Dto = {
  cIsUse: 'Y',
  cPlantId: '',
  cStoLocation: '',
  cStoType: '',
  cStoreHouse: '',
};

const getTitle = computed(() => {
  return formData.value?.id
    ? $t('ui.actionTitle.edit', [$t('mes.basicData.tax2036.name')])
    : $t('ui.actionTitle.create', [$t('mes.basicData.tax2036.name')]);
});

const [Form, formApi] = useVbenForm({
  commonConfig: {
    componentProps: {
      class: 'w-full',
    },
  },
  layout: 'vertical',
  schema: useFormSchema(),
  showDefaultActions: false,
  wrapperClass: 'grid-cols-1 md:grid-cols-2',
});

function normalizeFormValues(values: Record<string, any>) {
  return {
    ...defaultValues,
    ...values,
  } as Tax2036Api.CreateUpdateTax2036Dto;
}

function resetForm() {
  formApi.resetForm();
  formApi.setValues(formData.value || defaultValues);
}

const [Modal, modalApi] = useVbenModal({
  async onConfirm() {
    const { valid } = await formApi.validate();
    if (!valid) return;

    const values = normalizeFormValues(await formApi.getValues());
    modalApi.lock();

    try {
      await (formData.value?.id
        ? updateTax2036(formData.value.id, values)
        : createTax2036(values));
      modalApi.close();
      emit('success');
    } finally {
      modalApi.lock(false);
    }
  },
  async onOpenChange(isOpen) {
    if (isOpen) {
      const data = modalApi.getData<Tax2036Api.Tax2036>();
      formData.value = data?.id ? data : undefined;

      formApi.setState({ schema: useFormSchema(!!formData.value?.id) });
      formApi.resetForm();
      await new Promise((resolve) => setTimeout(resolve, 100));
      formApi.setValues(formData.value || defaultValues);
    }
  },
});
</script>

<template>
  <Modal :title="getTitle">
    <div class="max-h-[70vh] overflow-y-auto px-1">
      <Form class="mx-4" />
    </div>
    <template #prepend-footer>
      <div class="flex-auto">
        <Button type="primary" danger @click="resetForm">
          {{ $t('common.reset') }}
        </Button>
      </div>
    </template>
  </Modal>
</template>

文件名: data.ts

路径: E:\MyProject\mms\mms_client\apps\web-antd\src\views\mes\basic-data\tax-2036\data.ts

代码:

ts 复制代码
import type { VbenFormSchema } from '#/adapter/form';
import type { OnActionClickFn, VxeTableGridColumns } from '#/adapter/vxe-table';
import type { Tax0004Api, Tax2036Api } from '#/api';

import { getPopupContainer } from '@vben/utils';

import { getTax0004List } from '#/api';
import { $t } from '#/locales';

async function getPlantOptions() {
  const result = await getTax0004List({
    MaxResultCount: 1000,
    SkipCount: 0,
  });

  return (result.items || []).map((plant: Tax0004Api.Tax0004) => ({
    label: `${plant.cPlantId} - ${plant.cPlantNm}`,
    value: plant.cPlantId,
  }));
}

export function useGridFormSchema(): VbenFormSchema[] {
  return [
    {
      component: 'ApiSelect',
      componentProps: {
        allowClear: true,
        api: getPlantOptions,
        class: 'w-full',
        getPopupContainer,
        optionFilterProp: 'label',
        showSearch: true,
      },
      fieldName: 'cPlantId',
      label: $t('mes.basicData.tax2036.cPlantId'),
    },
    {
      component: 'Input',
      fieldName: 'cStoreHouse',
      label: $t('mes.basicData.tax2036.cStoreHouse'),
    },
    {
      component: 'Input',
      fieldName: 'cStoLocation',
      label: $t('mes.basicData.tax2036.cStoLocation'),
    },
    {
      component: 'Input',
      fieldName: 'cStoType',
      label: $t('mes.basicData.tax2036.cStoType'),
    },
    {
      component: 'Checkbox',
      defaultValue: false,
      fieldName: 'includeInvalidData',
      hideLabel: true,
      renderComponentContent: () => ({
        default: $t('mes.basicData.tax2036.isUsing'),
      }),
    },
  ];
}

export function useFormSchema(readonlyCode = false): VbenFormSchema[] {
  return [
    {
      component: 'ApiSelect',
      componentProps: {
        allowClear: true,
        api: getPlantOptions,
        class: 'w-full',
        getPopupContainer,
        optionFilterProp: 'label',
        showSearch: true,
      },
      fieldName: 'cPlantId',
      label: $t('mes.basicData.tax2036.cPlantId'),
      rules: 'required',
    },
    {
      component: 'Input',
      fieldName: 'cStoreHouse',
      label: $t('mes.basicData.tax2036.cStoreHouse'),
      rules: 'required',
    },
    {
      component: 'Input',
      componentProps: {
        readonly: readonlyCode,
      },
      fieldName: 'cStoLocation',
      label: $t('mes.basicData.tax2036.cStoLocation'),
      rules: 'required',
    },
    {
      component: 'Input',
      fieldName: 'cStoType',
      label: $t('mes.basicData.tax2036.cStoType'),
      rules: 'required',
    },
    {
      component: 'Input',
      fieldName: 'cTlDj',
      label: $t('mes.basicData.tax2036.cTlDj'),
    },
    {
      component: 'Input',
      fieldName: 'cStoLocationTxt',
      label: $t('mes.basicData.tax2036.cStoLocationTxt'),
    },
    {
      component: 'Input',
      defaultValue: 'Y',
      fieldName: 'cIsUse',
      label: $t('mes.basicData.tax2036.cIsUse'),
      rules: 'required',
    },
    {
      component: 'Textarea',
      componentProps: {
        rows: 3,
      },
      fieldName: 'cRemark',
      formItemClass: 'md:col-span-2',
      label: $t('mes.basicData.tax2036.cRemark'),
    },
  ];
}

export function useColumns<T = Tax2036Api.Tax2036>(
  onActionClick: OnActionClickFn<T>,
): VxeTableGridColumns {
  return [
    {
      field: 'cPlantId',
      minWidth: 160,
      title: $t('mes.basicData.tax2036.cPlantId'),
    },
    {
      field: 'cStoreHouse',
      minWidth: 160,
      title: $t('mes.basicData.tax2036.cStoreHouse'),
    },
    {
      field: 'cStoLocation',
      minWidth: 140,
      title: $t('mes.basicData.tax2036.cStoLocation'),
    },
    {
      field: 'cStoType',
      minWidth: 140,
      title: $t('mes.basicData.tax2036.cStoType'),
    },
    {
      field: 'cTlDj',
      minWidth: 150,
      title: $t('mes.basicData.tax2036.cTlDj'),
    },
    {
      field: 'cStoLocationTxt',
      minWidth: 180,
      title: $t('mes.basicData.tax2036.cStoLocationTxt'),
    },
    {
      field: 'cIsUse',
      minWidth: 100,
      title: $t('mes.basicData.tax2036.cIsUse'),
    },
    {
      field: 'cRemark',
      minWidth: 180,
      title: $t('mes.basicData.tax2036.cRemark'),
    },
    {
      field: 'creationTime',
      formatter: ({ cellValue }: { cellValue: string }) => {
        if (!cellValue) return '-';
        return new Date(cellValue).toLocaleString();
      },
      minWidth: 180,
      title: $t('common.createTime'),
    },
    {
      align: 'center',
      cellRender: {
        attrs: {
          nameField: 'cStoLocation',
          nameTitle: $t('mes.basicData.tax2036.name'),
          onClick: onActionClick,
        },
        name: 'CellOperation',
      },
      field: 'operation',
      fixed: 'right',
      title: $t('mes.basicData.tax2036.operation'),
      width: 130,
    },
  ];
}

文件名: list.vue

路径: E:\MyProject\mms\mms_client\apps\web-antd\src\views\mes\basic-data\tax-2036\list.vue

代码:

vue 复制代码
<script lang="ts" setup>
import type {
  OnActionClickParams,
  VxeTableGridOptions,
} from '#/adapter/vxe-table';
import type { Tax2036Api } from '#/api';

import { Page, useVbenModal } from '@vben/common-ui';
import { Plus } from '@vben/icons';

import { Button, message } from 'ant-design-vue';

import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
  deleteTax2036,
  getTax2036ById,
  getTax2036List,
} from '#/api';
import { $t } from '#/locales';

import { useColumns, useGridFormSchema } from './data';
import Form from './modules/form.vue';

const [FormModal, formModalApi] = useVbenModal({
  centered: true,
  class: 'w-[920px]',
  connectedComponent: Form,
  destroyOnClose: true,
  fullscreenButton: false,
});

const [Grid, gridApi] = useVbenVxeGrid({
  formOptions: {
    schema: useGridFormSchema(),
    submitOnChange: true,
  },
  gridOptions: {
    columns: useColumns(onActionClick),
    height: 'auto',
    keepSource: true,
    proxyConfig: {
      ajax: {
        query: async ({ page }, formValues) => {
          const params: Record<string, any> = {
            MaxResultCount: page.pageSize,
            SkipCount: (page.currentPage - 1) * page.pageSize,
          };

          if (formValues.cPlantId) {
            params.CPlantId = formValues.cPlantId;
          }

          if (formValues.cStoreHouse) {
            params.CStoreHouse = formValues.cStoreHouse;
          }

          if (formValues.cStoLocation) {
            params.CStoLocation = formValues.cStoLocation;
          }

          if (formValues.cStoType) {
            params.CStoType = formValues.cStoType;
          }

          params.IncludeInvalidData = formValues.includeInvalidData === true;

          const result = await getTax2036List(params);
          return {
            items: result.items || [],
            total: result.totalCount || 0,
          };
        },
      },
    },
    rowConfig: {
      keyField: 'id',
    },
    toolbarConfig: {
      custom: true,
      export: false,
      refresh: true,
      search: true,
      zoom: true,
    },
  } as VxeTableGridOptions<Tax2036Api.Tax2036>,
});

function onActionClick(e: OnActionClickParams<Tax2036Api.Tax2036>) {
  switch (e.code) {
    case 'delete': {
      onDelete(e.row);
      break;
    }
    case 'edit': {
      onEdit(e.row);
      break;
    }
  }
}

async function onEdit(row: Tax2036Api.Tax2036) {
  const hideLoading = message.loading({
    content: $t('mes.basicData.tax2036.loading', [row.cStoLocation]),
    duration: 0,
    key: 'action_process_msg',
  });

  try {
    const data = await getTax2036ById(row.id);
    formModalApi.setData(data).open();
    hideLoading();
  } catch {
    hideLoading();
  }
}

function onDelete(row: Tax2036Api.Tax2036) {
  const hideLoading = message.loading({
    content: $t('ui.actionMessage.deleting', [row.cStoLocation]),
    duration: 0,
    key: 'action_process_msg',
  });
  deleteTax2036(row.id)
    .then(() => {
      message.success({
        content: $t('ui.actionMessage.deleteSuccess', [row.cStoLocation]),
        key: 'action_process_msg',
      });
      onRefresh();
    })
    .catch(() => {
      hideLoading();
    });
}

function onRefresh() {
  gridApi.query();
}

function onCreate() {
  formModalApi.setData(null).open();
}
</script>

<template>
  <Page auto-content-height>
    <FormModal @success="onRefresh" />
    <Grid :table-title="$t('mes.basicData.tax2036.list')">
      <template #toolbar-tools>
        <Button type="primary" @click="onCreate">
          <Plus class="size-5" />
          {{ $t('ui.actionTitle.create', [$t('mes.basicData.tax2036.name')]) }}
        </Button>
      </template>
    </Grid>
  </Page>
</template>

三、效果展示

四、资源链接