Write the Code. Change the World.

分类目录
8月 23

列举列举

interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "applet": HTMLAppletElement;
    "area": HTMLAreaElement;
    "article": HTMLElement;
    "aside": HTMLElement;
    "audio": HTMLAudioElement;
    "b": HTMLElement;
    "base": HTMLBaseElement;
    "bdi": HTMLElement;
    "bdo": HTMLElement;
    "blockquote": HTMLQuoteElement;
    "body": HTMLBodyElement;
    "br": HTMLBRElement;
    "button": HTMLButtonElement;
    "canvas": HTMLCanvasElement;
    "caption": HTMLTableCaptionElement;
    "cite": HTMLElement;
    "code": HTMLElement;
    "col": HTMLTableColElement;
    "colgroup": HTMLTableColElement;
    "data": HTMLDataElement;
    "datalist": HTMLDataListElement;
    "dd": HTMLElement;
    "del": HTMLModElement;
    "details": HTMLDetailsElement;
    "dfn": HTMLElement;
    "dialog": HTMLDialogElement;
    "dir": HTMLDirectoryElement;
    "div": HTMLDivElement;
    "dl": HTMLDListElement;
    "dt": HTMLElement;
    "em": HTMLElement;
    "embed": HTMLEmbedElement;
    "fieldset": HTMLFieldSetElement;
    "figcaption": HTMLElement;
    "figure": HTMLElement;
    "font": HTMLFontElement;
    "footer": HTMLElement;
    "form": HTMLFormElement;
    "frame": HTMLFrameElement;
    "frameset": HTMLFrameSetElement;
    "h1": HTMLHeadingElement;
    "h2": HTMLHeadingElement;
    "h3": HTMLHeadingElement;
    "h4": HTMLHeadingElement;
    "h5": HTMLHeadingElement;
    "h6": HTMLHeadingElement;
    "head": HTMLHeadElement;
    "header": HTMLElement;
    "hgroup": HTMLElement;
    "hr": HTMLHRElement;
    "html": HTMLHtmlElement;
    "i": HTMLElement;
    "iframe": HTMLIFrameElement;
    "img": HTMLImageElement;
    "input": HTMLInputElement;
    "ins": HTMLModElement;
    "kbd": HTMLElement;
    "label": HTMLLabelElement;
    "legend": HTMLLegendElement;
    "li": HTMLLIElement;
    "link": HTMLLinkElement;
    "main": HTMLElement;
    "map": HTMLMapElement;
    "mark": HTMLElement;
    "marquee": HTMLMarqueeElement;
    "menu": HTMLMenuElement;
    "meta": HTMLMetaElement;
    "meter": HTMLMeterElement;
    "nav": HTMLElement;
    "noscript": HTMLElement;
    "object": HTMLObjectElement;
    "ol": HTMLOListElement;
    "optgroup": HTMLOptGroupElement;
    "option": HTMLOptionElement;
    "output": HTMLOutputElement;
    "p": HTMLParagraphElement;
    "param": HTMLParamElement;
    "picture": HTMLPictureElement;
    "pre": HTMLPreElement;
    "progress": HTMLProgressElement;
    "q": HTMLQuoteElement;
    "rp": HTMLElement;
    "rt": HTMLElement;
    "ruby": HTMLElement;
    "s": HTMLElement;
    "samp": HTMLElement;
    "script": HTMLScriptElement;
    "section": HTMLElement;
    "select": HTMLSelectElement;
    "slot": HTMLSlotElement;
    "small": HTMLElement;
    "source": HTMLSourceElement;
    "span": HTMLSpanElement;
    "strong": HTMLElement;
    "style": HTMLStyleElement;
    "sub": HTMLElement;
    "summary": HTMLElement;
    "sup": HTMLElement;
    "table": HTMLTableElement;
    "tbody": HTMLTableSectionElement;
    "td": HTMLTableDataCellElement;
    "template": HTMLTemplateElement;
    "textarea": HTMLTextAreaElement;
    "tfoot": HTMLTableSectionElement;
    "th": HTMLTableHeaderCellElement;
    "thead": HTMLTableSectionElement;
    "time": HTMLTimeElement;
    "title": HTMLTitleElement;
    "tr": HTMLTableRowElement;
    "track": HTMLTrackElement;
    "u": HTMLElement;
    "ul": HTMLUListElement;
    "var": HTMLElement;
    "video": HTMLVideoElement;
    "wbr": HTMLElement;
}
8月 23

很多很多年前,网页端图片裁切都会想到 cropper.js 。到了当前, cropper.js 还是值得一选。这个是基于 js,ts 的插件,不局限你使用 vue 还是 使用 react 或其他的。反正,还是很好用。当下 2.0 版本更好用。

官网: https://fengyuanchen.github.io/cropperjs/v2/zh/

演练场:https://fengyuanchen.github.io/cropperjs/v2/zh/playground.html

这个插件越来越自由,也越来越集中。它只关注输入(img dom)和输出(canvas)[转换成 base64 可能会存在跨域问题]。大只分以下几个模块(每个模块都可以单独使用)。

Cropper

CropperCanvas

CropperImage

CropperSelection

CropperShade

CropperGrid

CropperViewer
# 等等

这些,都各自有各自的渲染职责。加上就有,配置就改变。不加则没有。想加就加,想减就减。是真的好用。 先看一个我做的一个 demo。是基于vue3 + ts 实现的。(不对。里边还加了一些 element-plus 的组件)

构建的时候,也很方便。使用其接口就可以。更好的是是基于组件的方式来处理。最终的目的是什么,是要渲染到 dom 中,让人看到。 组件不就是最直接的方式吗。这个时候,你可以很方便的使用 css 去美化你的产品。只需要少量关注一些配置就好。比如裁切比例,覆盖比列等。

继续阅读

8月 17

有封装好的 useDark useToggle 这些插件,做项目就很方便。可是用的时候,会遇到一些问题。其实到最后,不是问题了。比如改变主题的时候,transition 失效了。因为没 transition,强行改变背景颜色等就会很生硬。transition 失效是因为 userColorMode 在处理改变的时候,先禁用 transition 再取消禁用导致的。在创建 useDark 的时候,options 的 disableTransition 可以控制该行为,只是不传值的时候,默认就是 true,就是禁用 trasition。那么默认传入 disableTransition 为 false 的值就可以搞定了。

https://github.com/vueuse/vueuse/blob/main/packages/core/useColorMode/index.ts#L122

const isDark = useDark({disableTransition: false})
 const toggleDark = useToggle(isDark)

只需上边这样实现即可。

继续阅读

8月 13

经过前边的处理,用户登录,侧边栏渲染,路由跳转,登录信息展示,用户退出。这些功能最小化完成。如下图所示。

在线预览,可切换角色。新用户注册,有一个默认角色。只有测试账号有多角色。

https://www.zeipan.com/admin

用户登录

打开后台域名任意 url 。在路由全局守卫中。 如果用户已登录(存在 token),先判断用户信息是否初始化,如果没有,先请求用户信息(返回数据不仅有用户信息,还有该用户对应的后台权限【用户生产菜单】),处理存储用户信息,添加路由信息。再重定向。如果已初始化,就继续。如果用户没登录,先重定向到登录页面。

调用接口,请求登录。获取用户的 token。存储 token。

通过 token,请求用户信息(数据中包括用户的权限数据),存储用户信息以及权限数据。处理权限数据,生成菜单。

8月 04

做了一些基础的配置。现在准备开始做布局了。这里只要两种布局,空白布局和后台布局。空白布局什么时候用呢,登录页面会用到。后台布局,就后台真正内容会用到。始终喜欢极简风格,所以方方面面都不要搞的太复杂。后台布局页面主要有左侧菜单栏,顶部信息栏,中间内如区域构成。

要做好这些,先温习一下 vue3 的 vue-router 以及 transition 还有 element plus 的 menu 组件。当然,loading 相关的也会考虑进去。

额外一点。左侧菜单的内容(和路由会一一映射),请求 api 的权限,全部有后端生成。前端不做权限的控制。也就是后端返回过来的菜单是什么样子,前端就渲染成什么样子。

https://router.vuejs.org/zh/

https://cn.vuejs.org/guide/built-ins/transition.html

https://element-plus.org/zh-CN/component/menu.html#collapse-%E6%8A%98%E5%8F%A0%E9%9D%A2%E6%9D%BF

基础布局

先看看构成后台的三部分。这里建立了一个 layout,拆分成了三部分。

src/views
├── HomeView.vue
├── layouts
│   ├── components
│   │   ├── AppMain
│   │   │   └── AppMain.vue
│   │   ├── NavBar
│   │   │   └── NavBar.vue
│   │   └── SideBar
│   │       └── SideBar.vue
│   └── index.vue
└── test.vue

路由是这样的。

import { createRouter, createWebHistory } from 'vue-router'
import HomeView from '../views/HomeView.vue'
import Layout from '@/views/layouts/index.vue'

const router = createRouter({
  history: createWebHistory(import.meta.env.BASE_URL),
  routes: [
    {
      path: '/',
      name: 'home',
      component: HomeView
    }, 
    {
      path: '/test',
      name: 'test',
      component: Layout,
      children: [
        {
          path: '',
          name: 'test-child',
          component: () => import('@/views/test.vue')
        }
      ]
    }
  ]
})

export default router

这里只是初步尝试,没有做更细致的逻辑编写。

希望你的屏幕足够宽,希望一屏足够承载你的业务。如果不够,如果不可以。那么滚动条在所难免。那么滚动条怎么出现最好看呢。

一种方式如上图所示。还有一种,可以只在 content 区域左右上下滚动。个人觉得那种不好看,交互也不是很好。最好就是一屏足够承载吧。

side bar 和 nav bar 都没使用背景色。滑动中间区域会看到穿透。真正做时,会有背景色,z-index 也会高出一些。

白天以及暗黑主题安排上,侧边栏展开收缩也安排上。

还有移动端的布局(小程序、h5、app 等等),特别是小程序。有的公司为了一套代码,给很多企业或多项目用。底部 tabbar 采用非原生写法,滚动条从顶滚到底,不仅滚动条丑,底部切换还会闪动。体验特不好。有时候为了好的滚动条效果,加入了 scroll-view 组件去控制。

这里,如果左侧菜单足够多,也会出现滚动条。这个在 side bar 内部去处理就好了。

继续阅读

8月 03

丰富的互联网资源,用 svg 文件作为 icon 已经很常见了。这里使用 iconify 来处理 icon。 iconify 可以理解为一个管理封装各个 icon 的工具。

https://iconify.design/

https://element-plus.org/zh-CN/component/icon.html

开始

# 安装
pnpm install unplugin-icons unplugin-vue-components -D

# 修改 vite.config.ts
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";

// https://vitejs.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': fileURLToPath(new URL('./src', import.meta.url))
    }
  },
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `
          @use "@/assets/styles/element.scss";
          @use "@/assets/styles/element-dark.scss";
          @use "@/assets/styles/main.scss";
          @use "@/assets/styles/preflight.scss";
        `,
      },
    },
  },
  plugins: [
    vue(),
    AutoImport({
      resolvers: [ElementPlusResolver({
        importStyle: "sass",
        directives: true
      })],
    }),
    Components({
      dts: "./components.d.ts", 
      resolvers: [
        IconsResolver({
          prefix: "i", // 默认为i,设置为false则不显示前缀
          enabledCollections: ["ep"],
          alias: {
            'icon': "ep", //配置别名
          }
        }),
        ElementPlusResolver({
          importStyle: "sass",
          directives: true
        })
      ],
    }),
    Icons({
      autoInstall: true, // 是否自动安装对应的图标库,默认为true
      scale: 1, // 图标缩放,默认为1
      defaultStyle: "", // 图标style
      defaultClass: "", // 图标class
      compiler: 'vue3', // 编译方式,可选值:'vue2', 'vue3', 'jsx'
      jsx: "react", // jsx风格:'react' or 'preact'
    })
  ]
})

继续阅读

8月 02

安装和使用 tailwindcss

https://blog.vini123.com/958

按照上边的链接的步骤,就可以安装好 tailwindcss 了。

只是在导入 scss 的时候,这里依旧写在 vite.config.ts 的配重中。

  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `
          @use "@/assets/styles/element.scss";
          @use "@/assets/styles/element-dark.scss";
          @use "@/assets/styles/main.scss";
        `,
      },
    },
  },

并在 package.json 中增加配置。遵循使用 es 模块规范。

{
  …,
  "type": "module"
}

运行起来,发现 tailwindcss 的默认样式已经生效了。

再完善 homeveiw,使用上 tailwindcss 看看。仅修改以下代码。

  <div class="container mx-auto">
    <h1 class="mt-4">Custom theme example (on demand)</h1>

发现,整个大的容器 div 已经水平居中了。 有了 tailwindcss,就可以单独少写很多 css。直接在 div 中就可以实现样式的效果。而作为后台,又使用 element-plus,需要使用到定制化的样式已经比较少了。 tailwindcss 就刚好使用了一点点就能达到想要的效果。

可是,再仔细看页面,发现按钮的文字看不见了,其他样式也有缺陷。检查发现是 tailwindcss 的样式对 element-plus 的样式有影响。

解决 tailwindcss 和 element-plus 样式的影响问题

先修改 tailwind.config.js 文件,加入以下配置。

export default {
  corePlugins: {
    preflight: false,
  },
  …

再去下载 preflight.css 文件,下载地址 [https://unpkg.com/browse/tailwindcss@3.3.3/src/css/preflight.css]() 注意,和自己安装版本的对应。

还是一样,改名为 prefight.scss 后,配置引入它。并注释掉有冲突的样式。这里把对按钮这块的处理样式给注释掉了。

// button,
// [type='button'],
// [type='reset'],
// [type='submit'] {
//   -webkit-appearance: button; /* 1 */
//   background-color: transparent; /* 2 */
//   background-image: none; /* 2 */
// }

然后发现,按钮的样式回来了。

提交版本

git add .
git commit -m '安装和配置 tailwindcss'
8月 02

前边一篇说了 element-plus 覆盖主题色的操作。这边就要做多主题的处理。也就是除了默认的主题,还得增加一个暗黑主题。

https://element-plus.org/zh-CN/guide/dark-mode.html

Do

思路还是一样,覆盖主题(颜色,间距等)。

# 创建
touch assets/styles/element-dark.scss

@forward 'element-plus/theme-chalk/src/dark/var.scss' with (
  $bg-color: (
    'page': #0a0a0a,
    '': #626aef,
    'overlay': #1d1e1f,
  ),
  $colors:(
    "primary": ("base": green),
  )
);

@use "element-plus/theme-chalk/src/dark/css-vars.scss" as *;

vite.config.ts 中引入。

    preprocessorOptions: {
      scss: {
        additionalData: `
          @use "@/assets/styles/element.scss";
          @use "@/assets/styles/element-dark.scss";
        `,
      },
    },

这样,样式就好了。可以手动在 html 节点加入 class="dark" 看看效果。发现背景颜色已经是黑色(#0a0a0a)了。

继续阅读

8月 01

多主题,是一个站点的常规配置。至少白天主题和夜晚主题是必须要有的。 element-plus 提供了一个很好的实现多主题的方案。先从修改默认主题色开始。

https://element-plus.org/zh-CN/guide/theming.html

开始

先安装 sass

pnpm add -D sass

再创建覆盖默认主题颜色的 sass 文件

# 创建
touch assets/styles/element.scss

# 填充以下内容
@forward 'element-plus/theme-chalk/src/common/var.scss' with (
  $colors: (
    'primary': (
      'base': #003261,
    ),
    'success': (
      'base': #21ba45,
    ),
    'warning': (
      'base': #f2711c,
    ),
    'danger': (
      'base': #db2828,
    ),
    'error': (
      'base': #db2828,
    ),
    'info': (
      'base': #42b8dd,
    ),
  ),
  $button-padding-horizontal: (
    'default': 80px,
  )
);

继续阅读

7月 06

安装 element-plus

https://element-plus.org/zh-CN/guide/installation.html

pnpm install element-plus

配置按需引入

首先安装 unplugin-vue-componentsunplugin-auto-import 这两款插件。

pnpm add -D unplugin-vue-components unplugin-auto-import

编辑 vite.config.ts,添加以下配置。

import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'

export default defineConfig({
  // ...
  plugins: [
    // ...
    AutoImport({
      resolvers: [ElementPlusResolver()],
    }),
    Components({
      resolvers: [ElementPlusResolver()],
    }),
  ],
})

继续阅读