vue3 admin 后台管理项目小计

Naive UI全局挂载useDialog、useMessage

App.vue

  <n-config-provider :locale="locale" :date-locale="dateLocale" :theme-overrides="themeOverrides">
    <n-dialog-provider>
      <n-message-provider>
        <router-view v-slot="{ Component }">
          <transition name="el-fade-in-linear" appear mode="out-in">
            <component :is="Component" />
          </transition>
        </router-view>
      </n-message-provider>
    </n-dialog-provider>
  </n-config-provider>

AppMain.vue

import { useDialog, useMessage } from 'naive-ui'
window.$useDialog = useDialog()
window.$message = useMessage()

global.d.ts

import type { DialogApiInjection } from 'naive-ui/lib/dialog/src/DialogProvider'
import type { MessageApiInjection } from 'naive-ui/lib/message/src/MessageProvider'
declare global {
  interface Window {
    $message: MessageApiInjection
    $useDialog: DialogApiInjection
  }
}

Naive UI Dialog 自定义 content render NInput输入框 + 校验

render 中 NInput 的value 必须定义onUpdateValue 才能更新视图

效果
在这里插入图片描述

import { NFormItem, type FormItemRule} from 'naive-ui'
...
  const controlRemark = ref('')
  const controlRemarkRef = ref<FormItemInst | null>(null)
  const rule: FormItemRule = {
      trigger: ['input', 'blur'],
      validator() {
        if (!controlRemark.value.trim()) {
          return new Error('你的校验提示语!')
        }
      }
    }
  window.$useDialog.warning({
      title: '提示',
      style: {
        width: '500px'
      },
      content: () =>
        h(
          'div',
          {
            class: 'text-base'
          },
          [
            h('div', ['您确认一些操作【', h('i', orderNo), '】记录?']),
            h(
              'div',
              { class: 'text-gray text-sm pt-1 pb-1' },
              'descdescdescdesc'
            ),
            h(
              NFormItem,
              {
                ref: controlRemarkRef,
                label: 'label',
                labelPlacement: 'left',
                rule
              },
              () =>
                h(NInput, {
                  clearable: true,
                  autofocus: true,
                  placeholder: 'placeholder',
                  type: 'textarea',
                  value: controlRemark.value,
                  maxlength: 255,
                  showCount: true,
                  onUpdateValue: (val: any) => {
                    controlRemark.value = val
                  },
                  class: 'text-left'
                })
            )
          ]
        ),
      positiveText: '确定',
      negativeText: '取消',
      iconPlacement: 'top',
      maskClosable: false,
      onPositiveClick: async () => {
        return new Promise((resolve, reject) => {
          controlRemarkRef.value?.validate('blur', (errors) => {
            if (errors) {
              reject()
            } else {
             ...your logic here
            }
          })
        })
      },
      onNegativeClick: async () => {
        return true
      }
    })

集成 IconPark 字节跳动开源图标库

为啥IconPark 而不是iconfont? iconfont前段时间挂了 & IconPark 图标还挺好看 & 尝点新鲜的
1.注册-创建项目-添加图标-生成在线链接
2.index.html header 新增一个script 引入链接
注册-创建项目-添加图标-生成在线链接
index.html  header 新增一个script 引入链接
常见问题 Failed to resolve component: iconpark-icon
解决方案 vite.config.ts

...
plugins:[
	   vue({
       	template: {
          compilerOptions: {
            isCustomElement: (tag) => tag === 'iconpark-icon'
          }
        }
      }),
       vueJsx({
        isCustomElement: (tag) => tag === 'iconpark-icon'
      }),
      ...
]

vite全局变量挂载、输出打包时间及环境|esbuild 移除console

vite.config.ts

const __APP_INFO__ = {
  pkg: { dependencies, devDependencies, name, version },
  lastBuildTime: moment().format('YYYY年MM月DD日 HH:mm:ss')
}
...
define: {
      // fix process is not defined https://github.com/vitejs/vite/issues/1973#issuecomment-787571499
      'process.platform': null,
      __APP_INFO__: JSON.stringify(__APP_INFO__)
    },
    ...
esbuild: {
      drop: mode === 'production' ? ['console', 'debugger'] : undefined
    },

main.ts

const { lastBuildTime, pkg } = __APP_INFO__
const viteMode = import.meta.env.MODE || 'production'
const title = pkg.name + ' ' + pkg.version
const content = viteMode + ' ' + lastBuildTime
console.log(
  `%c ${title} %c  ${content} `,
  `padding: 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060 ;`,
  `padding: 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e ;`
)

输出打包时间及环境

router-view/keep-alive 视图白屏问题

appMain.vue

    <router-view v-slot="{ Component, route }">
      <transition name="fade-transform" mode="out-in">
        <keep-alive :include="['a','b']">
          <component :is="Component" :key="route.path" />
        </keep-alive>
      </transition>
    </router-view>

初次进入路由时页面展示正常,再次进入时页面空白,devtools中keepAlive组件下为空
页面组件如下
表格组件封装Grid

<template>
    <Grid
      ref="gridRef"
      :columns="tableColumn"
      :toolbar-button="toolbarButtonConfig"
      :operte-button="operateButtonConfig"
      :query-api="order"
      :extra-query-params="extraQueryParams"
      @clickFallBack="clickFallBack"
    />
</template>
  1. 即使vue3支持template下有多个根节点 keepalive包裹的组件需要只有一个根元素 不能是fragment
  2. keepalive包裹的组件需设置name (unplugin-vue-define-options/vite 插件)
  3. (不建议)给router-view 设置key = route.fullPath 解决白屏 但keep-alive会失效

修改如下

defineOptions({
  name: 'Pool'
})
....
<template>
  <div>
    <Grid
      ref="gridRef"
      :columns="tableColumn"
      :toolbar-button="toolbarButtonConfig"
      :operte-button="operateButtonConfig"
      :query-api="order"
      :extra-query-params="extraQueryParams"
      @clickFallBack="clickFallBack"
    />
  </div>
</template>

异步路由 动态导入

路由资源由接口返回,需将资源中Component组件字符串转换为路由组件地址,实现动态导入
后端接口返回的路由资源树
vue2中的动态导入实现

// _import_production.js
module.exports = file => () => {
  file = file.indexOf('/views/') === 0 ? file.slice(7) : file
  let obj
  try {
    obj = import('@/views/' + file)
  } catch (e) {
    obj = import('@/views/errorPage/404')
  }
  return obj
}
....
// 遍历后台传来的路由字符串,转换为组件对象
route.component = _import(route.component); // 导入组件

vue3+viteGlob 导入

const Layout = () => import('@/layout/index.vue')
const modules = import.meta.glob(`@/views/**/**.vue`)
export const NotFoundRoutes: Array<RouterRowTy> = [{ path: '/:pathMatch(.*)', redirect: '/404', hidden: true }]
......
export const filterAsyncRouteForSSO = (resourceList: RouterRowTy[]) => {
  return resourceList.map((route: any) => {
    if (route.component === 'layout') {
      route.component = Layout
    } else {
      route.component = modules[route.component]
    }
    if (route.children && route.children.length) {
      route.children = filterAsyncRouteForSSO(route.children)
    }
    return route
  })
}
...
// 生成路由模块动态导入
let filterAsyncRoutes = filterAsyncRouteForSSO(resourceList)
// NotFoundRoutes 异常匹配路由  须放在路由匹配规则的最后
filterAsyncRoutes = [filterAsyncRoutes, NotFoundRoutes].flat()

vxe-table 列表自定义列配置、缓存列配置

vxe-table v4 列表自定义列配置(Promise缓存,拖动排序、置顶)

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值