>  기사  >  웹 프론트엔드  >  Vue3+ts를 사용하여 ProTable을 개발하는 방법

Vue3+ts를 사용하여 ProTable을 개발하는 방법

PHPz
PHPz앞으로
2023-05-12 21:10:121485검색

프런트 엔드 구현

성취 효과

Vue3+ts를 사용하여 ProTable을 개발하는 방법

기술 스택

vue3 + typescript + element-plus

Usage

<template>
  <el-tabs type="border-card" v-model="activeName">
    <el-tab-pane
    :label="item.label"
    v-for="(item, index) in templateConfig"
    :key="index" :name="item.name"
    lazy
    >
    <!--所有的 slot内容都在表格内部处理好, columnsType进行区分-->
    <pro-table
      :columns="item.columns"
      :type="item.name"
      :request-url="requestUrl"
    >
    </pro-table>
    </el-tab-pane>
  </el-tabs>
</template>
<script lang="ts" setup>
import { ref } from &#39;vue&#39;
import ProTable from &#39;./components/ProTable/index.vue&#39;
import { ColumnProps, RequestUrl } from &#39;./components/ProTable/types&#39;
import { projectConfig, projectConfigBatchDelete } from &#39;./service/api&#39;
const activeName = ref(&#39;user&#39;)
interface TemplateConfig {
  name: string
  label: string
  columns: ColumnProps[],
}
const requestUrl: RequestUrl = {
  create: projectConfig,
  list: projectConfig,
  update: projectConfig,
  destroy: projectConfig,
  batchDelete: projectConfigBatchDelete
}
const templateConfig = ref<TemplateConfig[]>([
  {
    label: &#39;ProTable&#39;,
    name: &#39;user&#39;,
    columns: [
      {
        key: &#39;userName&#39;,
        title: &#39;用户名&#39;,
        searchType: &#39;el-input&#39;
      },
      {
        key: &#39;password&#39;,
        title: &#39;密码&#39;,
        searchType: &#39;el-input&#39;
      },
      {
        key: &#39;email&#39;,
        title: &#39;邮箱&#39;,
        searchType: &#39;el-input&#39;
      },
      {
        key: &#39;phone&#39;,
        title: &#39;手机号&#39;,
        searchType: &#39;el-input&#39;
      },
      {
        key: &#39;role&#39;,
        title: &#39;角色&#39;,
        searchType: &#39;z-select&#39;,
        attrs: {
          options: [
            {
              label: &#39;管理员&#39;,
              value: &#39;admin&#39;
            },
            {
              label: &#39;普通用户&#39;,
              value: &#39;user&#39;
            }
          ]
        }
      },
      {
        key: &#39;status&#39;,
        title: &#39;状态&#39;,
        searchType: &#39;z-select&#39;,
        attrs: {
          options: [
            {
              label: &#39;启用&#39;,
              value: 1
            },
            {
              label: &#39;禁用&#39;,
              value: 0
            }
          ]
        },
        columnType: &#39;status&#39;
      },
      {
        key: &#39;hasUseArray&#39;,
        title: &#39;是否使用数组参数?&#39;,
        search: false,
        searchType: &#39;useExpandField&#39;,
        show: false,
        add: false
      },
      {
        key: &#39;arrayParams&#39;,
        title: &#39;数组参数&#39;,
        searchType: &#39;z-array&#39;,
        search: false,
        width: 120,
        add: false,
        show: false
      },
      {
        key: &#39;hasUseArray&#39;,
        title: &#39;是否使用JSON参数?&#39;,
        search: false,
        searchType: &#39;useExpandField&#39;,
        show: false,
        add: false
      },
      {
        key: &#39;jsonParams&#39;,
        title: &#39;JSON参数&#39;,
        searchType: &#39;z-json&#39;,
        search: false,
        width: 120,
        add: false,
        show: false
      },
      {
        key: &#39;createdAt&#39;,
        title: &#39;创建时间&#39;,
        width: 180,
        searchType: &#39;el-date-picker&#39;,
        add: false
      },
      {
        key: &#39;updatedAt&#39;,
        title: &#39;更新时间&#39;,
        width: 180,
        searchType: &#39;el-date-picker&#39;,
        add: false
      },
      {
        key: &#39;action&#39;,
        title: &#39;操作&#39;,
        search: false,
        add: false,
        width: 150
      }
    ]
  }
])
</script>
<style lang="less">
</style>

ProTable 디자인 아이디어

페이지는 5개 영역으로 나누어져 있으며,

  • 단일 검색 영역

  • 테이블 기능 버튼 영역

  • 테이블 오른쪽 상단의 작업 영역

  • 테이블 테마 영역

  • 테이블 페이지 매김 영역

어떤 문제를 고려해야 할까요?

  • 어떤 지역에서 들어오는 슬롯을 지원해야 합니까?

  • 양식의 원래 슬롯을 사용자에게 넘겨주어야 합니까, 아니면 내부적으로 캡슐화해야 합니까? 예를 들어 colum이 상태인 경우 tag에 매핑되어야 하고, array 유형인 경우에는 세부 정보를 보기 위해 클릭해야 하는 경우는 json인 테이블에 매핑됩니다. 각 테이블을 처리하는 것은 너무 번거로울 것입니다. 필드를 통해 제어하기를 바랍니다. colum是状态的时候需要映射成tag,是数组类型的时候映射成表格,是json的时候需要点击查看详情?假设每个表格都要处理的的话就太麻烦,我们希望通过一个字段来控制它。

  • column的某一列是否需要复制的功能?

  • 列字段需要编辑的功能?

实现的过程中有哪些细节?

  • 表格的高度,把可表格可视区域的大小交给用户自己来控制,把批量操作按钮放在最下面(fixed定位)。这样用户可以在最大区域内看到表格的内容。

编码风格

  • 组件上面属性如果超过三个,就换行

  • eslint使用的是standard风格。

css 小知识

<div class=&#39;box&#39;>
  <div class=&#39;z&#39;></div>
</div>
*{
  box-sizing: border-box;
}
.box{
  display: inline-block;
    vertical-align: top;
}
.z{
  height: 32px;
  border: 1px solid;
  width: 100px;
  display: inline-block;
}

如果把盒子变成了行内元素之后,若其内部还是行内元素,那么就会产生间隙,就会导致其高度与父元素高度不同。如下。

Vue3+ts를 사용하여 ProTable을 개발하는 방법

解决方法也很简单,则需要设置其子元素的vertical-align属性,或者设置font-size: 0,其根本原因是因为中间的文本元素也占位置。再或者不使用inline-block,换做inline-flex属性完全没有问题,因为在element-plus组件库中也大量的使用了这个属性,兼容性也很nice。

这个几个解决方法很早就知道了,就是关于vertical-algin这个,以及与line-height的关系还是有点模糊,一直也没去深究。

Vue3+ts를 사용하여 ProTable을 개발하는 방법

放两篇文章

CSS vertical-align 属性

还有联想到baseline这个东西在flex,align-items属性:交叉轴上的一个属性很像。

flex布局语法教程详解

表格操作

  • 添加数据之后,重新获取数据的时候pageIndex要重置为1,删除数据的时候也是一样。

  • 编辑数据的时候,pageIndex不变,还是当前页码。

  • 总结下来,就是当数据条数会发生改变的时候,都会重置pageIndex1。当用户操作不会影响数据总条数的时候,pageSize还维持当前不变。

小结

  • 使用了一个库,可以监听dom元素大小的变化,resize-observer-polyfill。

  • 在 3.x 中,如果一个元素同时定义了 v-bind="object" 和一个相同的独立 attribute。开发者可以自己选择要保留哪一个。

文档地址# v-bind 合并行为

参考文章

JavaScript API——ResizeObserver 的使用

后期功能扩展

  • 字段之间有关联关系情况的处理,暂时还没想好。

  • 扩展一下slot

  • 等等。。

迭代中....

Vue3+ts를 사용하여 ProTable을 개발하는 방법

后台实现

数据库 mysql

我这里使用的是 xampp

Vue3+ts를 사용하여 ProTable을 개발하는 방법 중 특정 열을 복사해야 하나요?

열 필드를 편집하는 기능이 필요합니까?

구현 과정의 세부사항은 어떻게 되나요? Vue3+ts를 사용하여 ProTable을 개발하는 방법

🎜🎜테이블 높이는 테이블의 보이는 영역의 크기를 사용자가 조절할 수 있도록 하고, 일괄 작업 버튼을 하단에 배치합니다(고정 위치 지정). 이렇게 하면 사용자는 가장 큰 영역에서 테이블의 내용을 볼 수 있습니다. 🎜🎜🎜🎜코딩 스타일🎜🎜🎜🎜구성 요소에 속성이 3개 이상 있으면 새 줄로 묶으세요.🎜🎜🎜🎜eslint는 표준 스타일을 사용합니다. 🎜🎜🎜🎜css 팁🎜
CREATE TABLE `project_config`  (
  `id` int NOT NULL AUTO_INCREMENT COMMENT &#39;主键&#39;,
  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT &#39;配置类型&#39;,
  `value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT &#39;配置的json字符串&#39;,
  `created_at` datetime NOT NULL,
  `updated_at` datetime NOT NULL DEFAULT &#39;0000-00-00 00:00:00&#39; ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 65 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = COMPACT;
npm init egg --type=simple
🎜상자가 인라인 요소로 바뀌어도 여전히 내부에 인라인 요소인 경우 간격이 발생하여 높이가 원래 상자와 달라집니다. 상위 요소의 높이입니다. 다음과 같이. 🎜🎜Vue3+ts를 사용하여 ProTable을 개발하는 방법🎜🎜솔루션 또한 매우 좋습니다. 간단하므로 하위 요소의 vertical-align 속성을 ​​설정하거나 font-size: 0을 설정해야 합니다. 근본적인 이유는 중간 텍스트입니다. 요소도 그 위치를 차지합니다. 또는 inline-block을 사용하는 대신 inline-flex 속성을 ​​사용하면 element-plus 컴포넌트 라이브러리. 이 속성을 사용하면 호환성도 매우 좋습니다. 🎜🎜저는 이러한 해결 방법을 오랫동안 알고 있었지만 vertical-alginline-height의 관계는 아직 다소 모호하고, 깊이 생각해 본 적이 없습니다. . 🎜🎜Vue3+ts를 사용하여 ProTable을 개발하는 방법🎜🎜두 개를 넣어주세요 기사 🎜🎜CSS 수직 정렬 속성🎜🎜 flexbaseline, align-items 속성도 생각합니다. 교차 축의 속성은 매우 유사합니다. 🎜🎜Flex 레이아웃 구문 튜토리얼에 대한 자세한 설명🎜🎜테이블 작업🎜🎜🎜🎜데이터를 추가한 후 데이터를 다시 가져올 때 pageIndex를 1로 재설정해야 하며, 데이터를 삭제할 때도 마찬가지입니다. 🎜🎜🎜🎜데이터 편집 시 pageIndex는 변경되지 않으며 여전히 현재 페이지 번호입니다. 🎜🎜🎜🎜 정리하자면, 데이터 항목 개수가 변경되면 pageIndex1로 재설정됩니다. 사용자 작업이 총 데이터 수에 영향을 미치지 않는 경우 pageSize는 변경되지 않고 유지됩니다. 🎜🎜🎜🎜요약🎜🎜🎜🎜dom 요소의 크기 변화를 모니터링할 수 있는 라이브러리인 resize-observer-polyfill을 사용합니다. 🎜🎜🎜🎜3.x에서 요소가 v-bind="object"와 동일한 독립 속성을 모두 정의하는 경우. 개발자는 어느 것을 유지할지 선택할 수 있습니다. 🎜🎜🎜🎜문서 주소 # v-bind 병합 동작🎜🎜참고 기사🎜🎜JavaScript API——ResizeObserver 사용법🎜🎜나중 함수 확장🎜🎜🎜🎜필드 간의 상관 관계를 처리하는 방법에 대해서는 생각해 본 적이 없습니다. 아직 . 🎜🎜🎜🎜 슬롯🎜🎜🎜🎜 등을 확장하세요. . 🎜🎜🎜🎜반복 중....🎜🎜Vue3+ts를 사용하여 다음을 수행하는 방법 development ProTable🎜🎜백엔드 구현🎜🎜database mysql🎜🎜xampp으로 설치한 것을 사용하고 있습니다. 버전을 확인해 보겠습니다. 이건 무슨 버전인가요? 가짜예요 정말 10인가요? 지금은 작동하는 한 걱정하지 마세요. 🎜🎜🎜🎜🎜테이블 만들기🎜
npm install --save egg-sequelize mysql2
🎜새 프로젝트🎜
&#39;use strict&#39;;
/** @type Egg.EggPlugin */
exports.sequelize = {
  enable: true,
  package: &#39;egg-sequelize&#39;,
};
exports.cors = {
  enable: true,
  package: &#39;egg-cors&#39;,
};
🎜프로젝트 디렉토리는 대략 다음과 같습니다🎜🎜🎜🎜

RESTful 风格的 URL 定义

Vue3+ts를 사용하여 ProTable을 개발하는 방법

Sequelize

npm install --save egg-sequelize mysql2
  • 在 config/plugin.js 中引入 egg-sequelize 插件, 这里我们引入了一个库egg-cors来帮我们实现cors

&#39;use strict&#39;;
/** @type Egg.EggPlugin */
exports.sequelize = {
  enable: true,
  package: &#39;egg-sequelize&#39;,
};
exports.cors = {
  enable: true,
  package: &#39;egg-cors&#39;,
};
  • 在 config/config.default.js 中编写 sequelize 配置

/* eslint valid-jsdoc: "off" */
&#39;use strict&#39;;
/**
 * @param {Egg.EggAppInfo} appInfo app info
 */
module.exports = appInfo => {
  /**
   * built-in config
   * @type {Egg.EggAppConfig}
   **/
  const config = exports = {};
  // use for cookie sign key, should change to your own and keep security
  config.keys = appInfo.name + &#39;_1655529530112_7627&#39;;
  // add your middleware config here
  config.middleware = [];
  config.security = {
    csrf: {
      enable: false,
      ignoreJSON: true,
    },
  };
  config.cors = {
    origin: &#39;*&#39;,
    allowMethods: &#39;GET,HEAD,PUT,POST,DELETE,PATCH&#39;,
  };
  // add your user config here
  const userConfig = {
    // myAppName: &#39;egg&#39;,
  };
  // sequelize
  const sequelize = {
    dialect: &#39;mysql&#39;,
    host: &#39;127.0.0.1&#39;,
    port: 3306,
    username: &#39;root&#39;,
    password: &#39;123456&#39;,
    database: &#39;test_database&#39;,
    timezone: &#39;+08:00&#39;,
    dialectOptions: {
      dateStrings: true,
      typeCast: true,
    },
    define: {
      freezeTableName: true, // 模型名强制和表明一致
      underscored: true, // 字段以下划线(_)来分割(默认是驼峰命名风格)
    },
  };
  return {
    ...config,
    ...userConfig,
    sequelize,
  };
};

1、时间格式化

类型需要采用:Sequelize.DATE

初始化Sequelize的时候传入dialectOptions参数,及timezone

timezone: &#39;+08:00&#39;,  // 改为标准时区
dialectOptions: {
  dateStrings: true,
  typeCast: true,
},

下面就开始编写

controller

对这块需要安装lodash,懂的都懂。

controller/ProjectConfig.js

&#39;use strict&#39;;
const { success } = require(&#39;../utils/res&#39;);
const { omit, pick } = require(&#39;lodash&#39;);
const Controller = require(&#39;egg&#39;).Controller;
class ProjectConfigController extends Controller {
  async index() {
    const { ctx } = this;
    const { pageSize, pageIndex } = ctx.query;
    const { Op, fn, col, where, literal } = this.app.Sequelize;
    // 固定的查询参数
    const stableQuery = pick(ctx.query, [ &#39;type&#39;, &#39;createdAt&#39;, &#39;updatedAt&#39; ]);
    const stableQueryArgs = Object.keys(stableQuery)
      .filter(key => Boolean(stableQuery[key]))
      .map(key => {
        return {
          [key]: stableQuery[key],
        };
      });
    const whereCondition = omit(ctx.query, [ &#39;pageIndex&#39;, &#39;pageSize&#39;, &#39;type&#39;, &#39;createdAt&#39;, &#39;updatedAt&#39; ]);
    // 需要模糊查询的参数
    const whereArgs = Object.keys(whereCondition)
      .filter(key => Boolean(whereCondition[key]))
      .map(key => {
        return where(fn(&#39;json_extract&#39;, col(&#39;value&#39;), literal(`\&#39;$.${key}\&#39;`)), {
          [Op.like]: `%${whereCondition[key]}%`,
        });
      });
    const query = {
      where: {
        [Op.and]: [
          ...stableQueryArgs,
          ...whereArgs,
        ],
      },
      order: [
        [ &#39;createdAt&#39;, &#39;DESC&#39; ],
      ],
      limit: Number(pageSize), // 每页显示数量
      offset: (pageIndex - 1) * pageSize, // 当前页数
    };
    const data = await ctx.model.ProjectConfig.findAndCountAll(query);
    ctx.body = success(data);
  }
  async create() {
    const { ctx } = this;
    const { type, value } = ctx.request.body;
    const data = await ctx.model.ProjectConfig.create({ type, value });
    ctx.body = success(data);
  }
  async update() {
    const { ctx } = this;
    const { type, value } = ctx.request.body;
    const { id } = ctx.params;
    const data = await ctx.model.ProjectConfig.update({ type, value }, { where: { id } });
    ctx.body = success(data);
  }
  async destroy() {
    const { ctx } = this;
    const { id } = ctx.params;
    console.log(id);
    const data = await ctx.model.ProjectConfig.destroy({ where: { id } });
    ctx.body = success(data);
  }
  async batchDestroy() {
    const { ctx } = this;
    const { ids } = ctx.request.body;
    console.log(ids);
    const { Op } = this.app.Sequelize;
    const data = await ctx.model.ProjectConfig.destroy({
      where: {
        id: {
          [Op.in]: ids,
        },
      },
    });
    ctx.body = success(data);
  }
}
module.exports = ProjectConfigController;

模糊查询

SELECT json_extract(字段名,&#39;$.json结构&#39;) FROM 表名;

sequelize高级查询

Post.findAll({
  where: sequelize.where(sequelize.fn(&#39;char_length&#39;, sequelize.col(&#39;content&#39;)), 7)
});
// SELECT ... FROM "posts" AS "post" WHERE char_length("content") = 7

中文文档,英文看的吃力,看中文的也无妨,不寒碜。^_^

model

model/project_config.js

&#39;use strict&#39;;
module.exports = app => {
  const { STRING, INTEGER, TEXT, DATE } = app.Sequelize;
  const ProjectConfig = app.model.define(&#39;project_config&#39;, {
    id: { type: INTEGER, primaryKey: true, autoIncrement: true },
    type: { type: STRING },
    value: {
      type: TEXT,
      get() {
        return this.getDataValue(&#39;value&#39;) ? JSON.parse(this.getDataValue(&#39;value&#39;)) : null;
      },
      set(value) {
        this.setDataValue(&#39;value&#39;, JSON.stringify(value));
      },
    },
    createdAt: { type: DATE },
    updatedAt: { type: DATE },
  });
  return ProjectConfig;
};

router.js

&#39;use strict&#39;;
/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller } = app;
  router.get(&#39;/api/projectConfig&#39;, controller.projectConfig.index);
  router.post(&#39;/api/projectConfig&#39;, controller.projectConfig.create);
  router.put(&#39;/api/projectConfig/:id&#39;, controller.projectConfig.update);
  router.delete(&#39;/api/projectConfig/:id&#39;, controller.projectConfig.destroy);
  router.post(&#39;/api/projectConfig/batchDelete&#39;, controller.projectConfig.batchDestroy);
};

API 文档 Apifox

先快速测试一把,然后去对前端代码。

Vue3+ts를 사용하여 ProTable을 개발하는 방법

ts用到的一些

  • 在类型别名(type alias)的声明中可以使用 keyoftypeofin 等关键字来进行一些强大的类型操作

interface A {
  x: number;
  y: string;
}
// 拿到 A 类型的 key 字面量枚举类型,相当于 type B = &#39;x&#39; | &#39;y&#39;
type B = keyof A;
const json = { foo: 1, bar: &#39;hi&#39; };
// 根据 ts 的类型推论生成一个类型。此时 C 的类型为 { foo: number; bar: string; }
type C = typeof json;
// 根据已有类型生成相关新类型,此处将 A 类型的所有成员变成了可选项,相当于 type D = { x?: number; y?: string; };
type D = {
  [T in keyof A]?: A[T];
};

在比如用一个联合类型来约束对象的key,用interface我就没实现,貌似.

export type FormItemType = &#39;el-input&#39; | &#39;z-select&#39; | &#39;el-date-picker&#39;
// 目前发现 interface 的key 只能是三种 string number symbol   keyof any
type IPlaceholderMapping = {
  [key in FormItemType]: string
}
export const placeholderMapping: IPlaceholderMapping = {
  &#39;el-input&#39;: &#39;请输入&#39;,
  &#39;z-select&#39;: &#39;请选择&#39;,
  &#39;el-date-picker&#39;: &#39;请选择日期&#39;
}

위 내용은 Vue3+ts를 사용하여 ProTable을 개발하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제