目錄
前台實作
實作效果
技術堆疊
使用方法
ProTable 設計想法
編碼風格
css 小知識
表格操作
小結
後期功能擴充
在後台實作
資料庫mysql
新專案
RESTful 风格的 URL 定义
Sequelize
controller
model
router.js
API 文档 Apifox
ts用到的一些
首頁 web前端 Vue.js 如何使用Vue3+ts開發ProTable

如何使用Vue3+ts開發ProTable

May 12, 2023 pm 09:10 PM
vue3 ts protable

前台實作

實作效果

如何使用Vue3+ts開發ProTable

技術堆疊

vue3 型態script element-plus

使用方法

<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個區域,

  • 表單搜尋區域

  • 表格功能按鈕區域

  • 表格右上角操作區域

  • #表格主題區域

  • 表格分頁區域

要考慮的問題?

  • 那些區域是要支援傳入slot的?

  • 表格原有的slot是否要交給使用者來傳遞,還是在內部進行封裝?如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安裝的,我們來看看版本。這是什麼版本?假的吧,真的到10了嗎?先不管了,能用就好。

如何使用Vue3+ts開發ProTable

建表

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

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中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌
威爾R.E.P.O.有交叉遊戲嗎?
1 個月前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

Vue3+TS+Vite開發技巧:如何進行SEO最佳化 Vue3+TS+Vite開發技巧:如何進行SEO最佳化 Sep 10, 2023 pm 07:33 PM

Vue3+TS+Vite開發技巧:如何進行SEO優化SEO(SearchEngineOptimization)是指透過優化網站的結構、內容和關鍵字等方面,使其在搜尋引擎的排名更靠前,從而增加網站的流量和曝光度。在Vue3+TS+Vite等現代前端技術的開發中,如何進行SEO最佳化是一個很重要的問題。本文將介紹一些Vue3+TS+Vite開發的技巧與方法,幫

vue3+vite:src使用require動態匯入圖片報錯怎麼解決 vue3+vite:src使用require動態匯入圖片報錯怎麼解決 May 21, 2023 pm 03:16 PM

vue3+vite:src使用require動態導入圖片報錯和解決方法vue3+vite動態的導入多張圖片vue3如果使用的是typescript開發,就會出現require引入圖片報錯,requireisnotdefined不能像使用vue2這樣imgUrl:require(' …/assets/test.png')導入,是因為typescript不支援require所以用import導入,下面介紹如何解決:使用awaitimport

vue3專案中怎麼使用tinymce vue3專案中怎麼使用tinymce May 19, 2023 pm 08:40 PM

tinymce是一個功能齊全的富文本編輯器插件,但在vue中引入tinymce並不像別的Vue富文本插件一樣那麼順利,tinymce本身並不適配Vue,還需要引入@tinymce/tinymce-vue,並且它是國外的富文本插件,沒有透過中文版本,需要在其官網下載翻譯包(可能需要翻牆)。 1.安裝相關依賴npminstalltinymce-Snpminstall@tinymce/tinymce-vue-S2、下載中文包3.引入皮膚和漢化包在項目public資料夾下新建tinymce資料夾,將下載的

Vue3如何實作刷新頁面局部內容 Vue3如何實作刷新頁面局部內容 May 26, 2023 pm 05:31 PM

想要實現頁面的局部刷新,我們只需要實現局部元件(dom)的重新渲染。在Vue中,想要實現這效果最簡單的方式方法就是使用v-if指令。在Vue2中我們除了使用v-if指令讓局部dom的重新渲染,也可以新建一個空白元件,需要刷新局部頁面時跳轉至這個空白元件頁面,然後在空白元件內的beforeRouteEnter守衛中又跳轉回原來的頁面。如下圖所示,如何在Vue3.X中實現點擊刷新按鈕實現紅框範圍內的dom重新加載,並展示對應的加載狀態。由於Vue3.X中scriptsetup語法中組件內守衛只有o

Vue3怎麼解析markdown並實現程式碼高亮顯示 Vue3怎麼解析markdown並實現程式碼高亮顯示 May 20, 2023 pm 04:16 PM

Vue實作部落格前端,需要實作markdown的解析,如果有程式碼則需要實作程式碼的高亮。 Vue的markdown解析函式庫很多,如markdown-it、vue-markdown-loader、marked、vue-markdown等。這些庫都大同小異。這裡選用的是marked,程式碼高亮的函式庫選用的是highlight.js。具體實現步驟如下:一、安裝依賴庫在vue專案下開啟命令窗口,並輸入以下命令npminstallmarked-save//marked用於將markdown轉換成htmlnpmins

Vue3+TS+Vite開發技巧:如何進行跨網域請求和網路請求最佳化 Vue3+TS+Vite開發技巧:如何進行跨網域請求和網路請求最佳化 Sep 09, 2023 pm 04:40 PM

Vue3+TS+Vite開發技巧:如何進行跨域請求和網路請求最佳化引言:在前端開發中,網路請求是非常常見的操作。如何優化網頁請求以提高頁面載入速度和使用者體驗是我們開發者需要思考的問題之一。同時,對於一些需要向不同網域發送請求的場景,我們需要解決跨域問題。本文將介紹如何在Vue3+TS+Vite開發環境下進行跨域請求以及網路請求的最佳化技巧。一、跨域請求解決方案使

Vue3+TS+Vite開發技巧:如何進行前端安全防護 Vue3+TS+Vite開發技巧:如何進行前端安全防護 Sep 09, 2023 pm 04:19 PM

Vue3+TS+Vite開發技巧:如何進行前端安全防護隨著前端技術的不斷發展,越來越多的企業和個人開始使用Vue3+TS+Vite進行前端開發。然而,隨之而來的安全風險也引起了人們的注意。在本文中,我們將探討一些常見的前端安全性問題,並分享一些在Vue3+TS+Vite開發過程中如何進行前端安全防護的技巧。輸入驗證使用者的輸入往往是前端安全漏洞的主要來源之一。在

Vue3+TS+Vite開發技巧:如何進行資料加密與存儲 Vue3+TS+Vite開發技巧:如何進行資料加密與存儲 Sep 10, 2023 pm 04:51 PM

Vue3+TS+Vite開發技巧:如何進行資料加密和儲存隨著網路技術的快速發展,資料的安全性和隱私保護變得越來越重要。在Vue3+TS+Vite開發環境下,如何進行資料加密和存儲,是每個開發人員都需要面對的問題。本文將介紹一些常用的資料加密和儲存的技巧,幫助開發人員提升應用程式的安全性和使用者體驗。一、資料加密前端資料加密前端加密是保護資料安全性的重要一環。常用

See all articles