vue3 + typescript + 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 'vue' import ProTable from './components/ProTable/index.vue' import { ColumnProps, RequestUrl } from './components/ProTable/types' import { projectConfig, projectConfigBatchDelete } from './service/api' const activeName = ref('user') 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: 'ProTable', name: 'user', columns: [ { key: 'userName', title: '用户名', searchType: 'el-input' }, { key: 'password', title: '密码', searchType: 'el-input' }, { key: 'email', title: '邮箱', searchType: 'el-input' }, { key: 'phone', title: '手机号', searchType: 'el-input' }, { key: 'role', title: '角色', searchType: 'z-select', attrs: { options: [ { label: '管理员', value: 'admin' }, { label: '普通用户', value: 'user' } ] } }, { key: 'status', title: '状态', searchType: 'z-select', attrs: { options: [ { label: '启用', value: 1 }, { label: '禁用', value: 0 } ] }, columnType: 'status' }, { key: 'hasUseArray', title: '是否使用数组参数?', search: false, searchType: 'useExpandField', show: false, add: false }, { key: 'arrayParams', title: '数组参数', searchType: 'z-array', search: false, width: 120, add: false, show: false }, { key: 'hasUseArray', title: '是否使用JSON参数?', search: false, searchType: 'useExpandField', show: false, add: false }, { key: 'jsonParams', title: 'JSON参数', searchType: 'z-json', search: false, width: 120, add: false, show: false }, { key: 'createdAt', title: '创建时间', width: 180, searchType: 'el-date-picker', add: false }, { key: 'updatedAt', title: '更新时间', width: 180, searchType: 'el-date-picker', add: false }, { key: 'action', title: '操作', search: false, add: false, width: 150 } ] } ]) </script> <style lang="less"> </style>
페이지는 5개 영역으로 나누어져 있으며,
단일 검색 영역
테이블 기능 버튼 영역
테이블 오른쪽 상단의 작업 영역
테이블 테마 영역
테이블 페이지 매김 영역
어떤 문제를 고려해야 할까요?
어떤 지역에서 들어오는 슬롯을 지원해야 합니까?
양식의 원래 슬롯을 사용자에게 넘겨주어야 합니까, 아니면 내부적으로 캡슐화해야 합니까? 예를 들어 colum
이 상태인 경우 tag
에 매핑되어야 하고, array
유형인 경우에는 세부 정보를 보기 위해 클릭해야 하는 경우는 json
인 테이블에 매핑됩니다. 각 테이블을 처리하는 것은 너무 번거로울 것입니다. 필드를 통해 제어하기를 바랍니다. colum
是状态的时候需要映射成tag
,是数组
类型的时候映射成表格,是json
的时候需要点击查看详情?假设每个表格都要处理的的话就太麻烦,我们希望通过一个字段来控制它。
有column
的某一列是否需要复制的功能?
列字段需要编辑的功能?
实现的过程中有哪些细节?
表格的高度,把可表格可视区域的大小交给用户自己来控制,把批量操作按钮放在最下面(fixed
定位)。这样用户可以在最大区域内看到表格的内容。
组件上面属性如果超过三个,就换行
eslint使用的是standard
风格。
<div class='box'> <div class='z'></div> </div>
*{ box-sizing: border-box; } .box{ display: inline-block; vertical-align: top; } .z{ height: 32px; border: 1px solid; width: 100px; display: inline-block; }
如果把盒子变成了行内
元素之后,若其内部还是行内元素,那么就会产生间隙,就会导致其高度与父元素高度不同。如下。
解决方法也很简单,则需要设置其子元素的vertical-align
属性,或者设置font-size: 0
,其根本原因是因为中间的文本元素也占位置。再或者不使用inline-block
,换做inline-flex
属性完全没有问题,因为在element-plus
组件库中也大量的使用了这个属性,兼容性也很nice。
这个几个解决方法很早就知道了,就是关于vertical-algin
这个,以及与line-height
的关系还是有点模糊,一直也没去深究。
放两篇文章
CSS vertical-align 属性
还有联想到baseline
这个东西在flex
,align-items属性:交叉轴上的一个属性很像。
flex布局语法教程详解
添加数据之后,重新获取数据的时候pageIndex
要重置为1,删除数据的时候也是一样。
编辑数据的时候,pageIndex
不变,还是当前页码。
总结下来,就是当数据条数会发生改变的时候,都会重置pageIndex
为1
。当用户操作不会影响数据总条数的时候,pageSize
还维持当前不变。
使用了一个库,可以监听dom元素大小的变化,resize-observer-polyfill。
在 3.x 中,如果一个元素同时定义了 v-bind="object"
和一个相同的独立 attribute。开发者可以自己选择要保留哪一个。
文档地址# v-bind 合并行为
参考文章
JavaScript API——ResizeObserver 的使用
字段之间有关联关系情况的处理,暂时还没想好。
扩展一下slot
等等。。
迭代中....
我这里使用的是 xampp
열
중 특정 열을 복사해야 하나요?
구현 과정의 세부사항은 어떻게 되나요?
🎜🎜테이블 높이는 테이블의 보이는 영역의 크기를 사용자가 조절할 수 있도록 하고, 일괄 작업 버튼을 하단에 배치합니다(고정
위치 지정). 이렇게 하면 사용자는 가장 큰 영역에서 테이블의 내용을 볼 수 있습니다. 🎜🎜🎜🎜코딩 스타일🎜🎜🎜🎜구성 요소에 속성이 3개 이상 있으면 새 줄로 묶으세요.🎜🎜🎜🎜eslint는 표준
스타일을 사용합니다. 🎜🎜🎜🎜css 팁🎜CREATE TABLE `project_config` ( `id` int NOT NULL AUTO_INCREMENT COMMENT '主键', `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '配置类型', `value` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '配置的json字符串', `created_at` datetime NOT NULL, `updated_at` datetime NOT NULL DEFAULT '0000-00-00 00:00:00' 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🎜상자가
인라인
요소로 바뀌어도 여전히 내부에 인라인 요소인 경우 간격이 발생하여 높이가 원래 상자와 달라집니다. 상위 요소의 높이입니다. 다음과 같이. 🎜🎜🎜🎜솔루션 또한 매우 좋습니다. 간단하므로 하위 요소의 vertical-align
속성을 설정하거나 font-size: 0
을 설정해야 합니다. 근본적인 이유는 중간 텍스트입니다. 요소도 그 위치를 차지합니다. 또는 inline-block
을 사용하는 대신 inline-flex
속성을 사용하면 element-plus
컴포넌트 라이브러리. 이 속성을 사용하면 호환성도 매우 좋습니다. 🎜🎜저는 이러한 해결 방법을 오랫동안 알고 있었지만 vertical-algin
과 line-height
의 관계는 아직 다소 모호하고, 깊이 생각해 본 적이 없습니다. . 🎜🎜🎜🎜두 개를 넣어주세요 기사 🎜🎜CSS 수직 정렬 속성🎜🎜 flex
의 baseline
, align-items 속성도 생각합니다. 교차 축의 속성은 매우 유사합니다. 🎜🎜Flex 레이아웃 구문 튜토리얼에 대한 자세한 설명🎜🎜테이블 작업🎜🎜🎜🎜데이터를 추가한 후 데이터를 다시 가져올 때 pageIndex
를 1로 재설정해야 하며, 데이터를 삭제할 때도 마찬가지입니다. 🎜🎜🎜🎜데이터 편집 시 pageIndex
는 변경되지 않으며 여전히 현재 페이지 번호입니다. 🎜🎜🎜🎜 정리하자면, 데이터 항목 개수가 변경되면 pageIndex
는 1
로 재설정됩니다. 사용자 작업이 총 데이터 수에 영향을 미치지 않는 경우 pageSize
는 변경되지 않고 유지됩니다. 🎜🎜🎜🎜요약🎜🎜🎜🎜dom 요소의 크기 변화를 모니터링할 수 있는 라이브러리인 resize-observer-polyfill을 사용합니다. 🎜🎜🎜🎜3.x에서 요소가 v-bind="object"
와 동일한 독립 속성을 모두 정의하는 경우. 개발자는 어느 것을 유지할지 선택할 수 있습니다. 🎜🎜🎜🎜문서 주소 # v-bind 병합 동작🎜🎜참고 기사🎜🎜JavaScript API——ResizeObserver 사용법🎜🎜나중 함수 확장🎜🎜🎜🎜필드 간의 상관 관계를 처리하는 방법에 대해서는 생각해 본 적이 없습니다. 아직 . 🎜🎜🎜🎜 슬롯
🎜🎜🎜🎜 등을 확장하세요. . 🎜🎜🎜🎜반복 중....🎜🎜🎜🎜백엔드 구현🎜🎜database mysql🎜🎜xampp
으로 설치한 것을 사용하고 있습니다. 버전을 확인해 보겠습니다. 이건 무슨 버전인가요? 가짜예요 정말 10인가요? 지금은 작동하는 한 걱정하지 마세요. 🎜🎜🎜🎜🎜테이블 만들기🎜npm install --save egg-sequelize mysql2🎜새 프로젝트🎜
'use strict'; /** @type Egg.EggPlugin */ exports.sequelize = { enable: true, package: 'egg-sequelize', }; exports.cors = { enable: true, package: 'egg-cors', };🎜프로젝트 디렉토리는 대략 다음과 같습니다🎜🎜🎜🎜
npm install --save egg-sequelize mysql2
在 config/plugin.js
中引入 egg-sequelize 插件, 这里我们引入了一个库egg-cors
来帮我们实现cors
。
'use strict'; /** @type Egg.EggPlugin */ exports.sequelize = { enable: true, package: 'egg-sequelize', }; exports.cors = { enable: true, package: 'egg-cors', };
在 config/config.default.js
中编写 sequelize 配置
/* eslint valid-jsdoc: "off" */ 'use strict'; /** * @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 + '_1655529530112_7627'; // add your middleware config here config.middleware = []; config.security = { csrf: { enable: false, ignoreJSON: true, }, }; config.cors = { origin: '*', allowMethods: 'GET,HEAD,PUT,POST,DELETE,PATCH', }; // add your user config here const userConfig = { // myAppName: 'egg', }; // sequelize const sequelize = { dialect: 'mysql', host: '127.0.0.1', port: 3306, username: 'root', password: '123456', database: 'test_database', timezone: '+08:00', dialectOptions: { dateStrings: true, typeCast: true, }, define: { freezeTableName: true, // 模型名强制和表明一致 underscored: true, // 字段以下划线(_)来分割(默认是驼峰命名风格) }, }; return { ...config, ...userConfig, sequelize, }; };
1、时间格式化
类型需要采用:Sequelize.DATE
初始化Sequelize的时候传入dialectOptions参数,及timezone
timezone: '+08:00', // 改为标准时区 dialectOptions: { dateStrings: true, typeCast: true, },
下面就开始编写
对这块需要安装lodash
,懂的都懂。
controller/ProjectConfig.js
'use strict'; const { success } = require('../utils/res'); const { omit, pick } = require('lodash'); const Controller = require('egg').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, [ 'type', 'createdAt', 'updatedAt' ]); const stableQueryArgs = Object.keys(stableQuery) .filter(key => Boolean(stableQuery[key])) .map(key => { return { [key]: stableQuery[key], }; }); const whereCondition = omit(ctx.query, [ 'pageIndex', 'pageSize', 'type', 'createdAt', 'updatedAt' ]); // 需要模糊查询的参数 const whereArgs = Object.keys(whereCondition) .filter(key => Boolean(whereCondition[key])) .map(key => { return where(fn('json_extract', col('value'), literal(`\'$.${key}\'`)), { [Op.like]: `%${whereCondition[key]}%`, }); }); const query = { where: { [Op.and]: [ ...stableQueryArgs, ...whereArgs, ], }, order: [ [ 'createdAt', 'DESC' ], ], 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(字段名,'$.json结构') FROM 表名;
sequelize高级查询
Post.findAll({ where: sequelize.where(sequelize.fn('char_length', sequelize.col('content')), 7) }); // SELECT ... FROM "posts" AS "post" WHERE char_length("content") = 7
中文文档,英文看的吃力,看中文的也无妨,不寒碜。^_^
model/project_config.js
'use strict'; module.exports = app => { const { STRING, INTEGER, TEXT, DATE } = app.Sequelize; const ProjectConfig = app.model.define('project_config', { id: { type: INTEGER, primaryKey: true, autoIncrement: true }, type: { type: STRING }, value: { type: TEXT, get() { return this.getDataValue('value') ? JSON.parse(this.getDataValue('value')) : null; }, set(value) { this.setDataValue('value', JSON.stringify(value)); }, }, createdAt: { type: DATE }, updatedAt: { type: DATE }, }); return ProjectConfig; };
'use strict'; /** * @param {Egg.Application} app - egg application */ module.exports = app => { const { router, controller } = app; router.get('/api/projectConfig', controller.projectConfig.index); router.post('/api/projectConfig', controller.projectConfig.create); router.put('/api/projectConfig/:id', controller.projectConfig.update); router.delete('/api/projectConfig/:id', controller.projectConfig.destroy); router.post('/api/projectConfig/batchDelete', controller.projectConfig.batchDestroy); };
先快速测试一把,然后去对前端代码。
在类型别名(type alias)的声明中可以使用 keyof
、typeof
、in
等关键字来进行一些强大的类型操作
interface A { x: number; y: string; } // 拿到 A 类型的 key 字面量枚举类型,相当于 type B = 'x' | 'y' type B = keyof A; const json = { foo: 1, bar: 'hi' }; // 根据 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 = 'el-input' | 'z-select' | 'el-date-picker' // 目前发现 interface 的key 只能是三种 string number symbol keyof any type IPlaceholderMapping = { [key in FormItemType]: string } export const placeholderMapping: IPlaceholderMapping = { 'el-input': '请输入', 'z-select': '请选择', 'el-date-picker': '请选择日期' }
위 내용은 Vue3+ts를 사용하여 ProTable을 개발하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!