vue3 + dactylographié + élément-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>
La page est divisée en 5 zones,
Zone de recherche unique
Zone des boutons de fonction du tableau
Zone d'opération dans le coin supérieur droit du tableau
Zone du thème du tableau
Zone de pagination du tableau
Quels problèmes doivent être pris en compte ?
Quelles zones doivent prendre en charge les créneaux entrants ?
L'emplacement d'origine du formulaire doit-il être remis à l'utilisateur pour livraison, ou doit-il être encapsulé en interne ? Par exemple, lorsque colum
est un statut, il doit être mappé à tag
, et lorsqu'il s'agit d'un type array
, il doit être mappé à une table, qui est json
Quand devez-vous cliquer pour afficher les détails ? Il serait trop fastidieux de traiter chaque table. Nous espérons la contrôler via un champ. 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
Est-ce qu'une certaine colonne de colonne
doit être copiée ?
Quels sont les détails du processus de mise en œuvre ?
🎜🎜Pour la hauteur de la table, laissez l'utilisateur contrôler la taille de la zone visible de la table, et placez le bouton d'opération par lots en bas (positionnementfixe
). De cette façon, l'utilisateur peut voir le contenu du tableau dans la plus grande zone. 🎜🎜🎜🎜Style de codage🎜🎜🎜🎜S'il y a plus de trois attributs sur le composant, enveloppez-les dans de nouvelles lignes🎜🎜🎜🎜eslint utilise le style standard
. 🎜🎜🎜🎜Conseils 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
inline
, si elle est toujours un élément en ligne à l'intérieur, un espace se produira, ce qui fera que sa hauteur sera différente de la hauteur de l'élément parent. comme suit. 🎜🎜🎜🎜La solution est également très bon. Simple, vous devez définir l'attribut vertical-align
de ses éléments enfants, ou définir font-size: 0
. La raison fondamentale est que le texte du milieu. L'élément occupe également la position. Alternativement, au lieu d'utiliser inline-block
, utilisez l'attribut inline-flex
. Il n'y a aucun problème, car il y en a également un grand nombre dans le vertical-algin
et line-height
est encore un peu vague, et je ne m'y suis jamais penchée . 🎜🎜🎜🎜Mettez deux articles 🎜🎜Attribut CSS vertical-align🎜🎜Je pense aussi à baseline
dans flex
, attribut align-items : un attribut sur l'axe transversal est très similaire. 🎜🎜Explication détaillée du didacticiel de syntaxe de mise en page flexible🎜🎜Opération de table🎜🎜🎜🎜Après l'ajout de données, pageIndex
doit être réinitialisé à 1 lors de la réacquisition de données, et il en va de même lors de la suppression de données. 🎜🎜🎜🎜Lors de l'édition des données, pageIndex
ne change pas, c'est toujours le numéro de page actuel. 🎜🎜🎜🎜Pour résumer, lorsque le nombre d'éléments de données change, pageIndex
sera réinitialisé à 1
. Lorsque les opérations utilisateur n'affectent pas le nombre total de données, pageSize
reste inchangé. 🎜🎜🎜🎜Résumé🎜🎜🎜🎜À l'aide d'une bibliothèque capable de surveiller les changements dans la taille des éléments dom, resize-observer-polyfill. 🎜🎜🎜🎜Dans 3.x, si un élément définit à la fois v-bind="object"
et un attribut indépendant identique. Les développeurs peuvent choisir lequel conserver. 🎜🎜🎜🎜Adresse du document # comportement de fusion v-bind🎜🎜Article de référence🎜🎜API JavaScript——Utilisation de ResizeObserver🎜🎜Extension ultérieure des fonctions🎜🎜🎜🎜Comment gérer la corrélation entre les champs, je n'y ai pas pensé encore . 🎜🎜🎜🎜Agrandissez le emplacement
🎜🎜🎜🎜 et ainsi de suite. . 🎜🎜🎜🎜Itération....🎜🎜🎜🎜Implémentation backend🎜🎜base de données mysql🎜🎜J'utilise celle installée par xampp
. Vérifions la version. De quelle version s'agit-il ? C'est faux, est-ce vraiment 10 ? Ne vous inquiétez pas pour l'instant, tant que cela fonctionne. 🎜🎜🎜🎜🎜Construire une table🎜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': '请选择日期' }
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!