0
点赞
收藏
分享

微信扫一扫

为什么我的微信小程序 窗口背景色backgroundColor设置参数 无效的问题处理记录!

云竹文斋 03-29 11:30 阅读 3

一、创建项目

1.1 技术栈

1.2  vite 项目初始化

npm init vite@latest vue3-element-admin --template vue-ts

 1.3 src 路径别名配置

Vite 配置

配置 vite.config.ts 

// https://vitejs.dev/config/

import { UserConfig, ConfigEnv, loadEnv, defineConfig } from 'vite'
import path from 'path'
import vue from '@vitejs/plugin-vue'

const pathSrc = path.resolve(__dirname, 'src')
export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
  return {
    // 路径别名
    resolve: {
      alias: {
        '@': pathSrc
      }
    },
    plugins: [vue()]
  }
})

 安装@types/node

import path from 'path'编译器报错:TS2307: Cannot find module 'path' or its corresponding type declarations.

本地安装 Node 的 TypeScript 类型描述文件即可解决编译器报错

npm install @types/node --save-dev

TypeScript 编译配置

同样还是import path from 'path' 编译报错: TS1259: Module '"path"' can only be default-imported using the 'allowSyntheticDefaultImports' flag

因为 typescript 特殊的 import 方式 , 需要配置允许默认导入的方式,还有路径别名的配置


// tsconfig.json
{
  "compilerOptions": {
    "baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
    "paths": { //路径映射,相对于baseUrl
      "@/*": ["src/*"] 
    },
    "allowSyntheticDefaultImports": true // 允许默认导入
  }
}

1.4 unplugin 自动导入

Element Plus 官方文档中推荐 按需自动导入 的方式,而此需要使用额外的插件 unplugin-auto-importunplugin-vue-components 来导入要使用的组件。所以在整合 Element Plus 之前先了解下自动导入的概念和作用

概念

为了避免在多个页面重复引入 API 或 组件,由此而产生的自动导入插件来节省重复代码和提高开发效率。

安装插件依赖

npm install -D unplugin-auto-import unplugin-vue-components 

vite.config.ts - 自动导入配置

先创建好 /src/types 目录用于存放自动导入函数和组件的TS类型声明文件,再进行自动导入配置

// vite.config.ts
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";

import path from "path";

const pathSrc = path.resolve(__dirname, "src");

plugins: [
  AutoImport({
    // 自动导入 Vue 相关函数,如:ref, reactive, toRef 等
    imports: ["vue"],
    eslintrc: {
      enabled: true, // 是否自动生成 eslint 规则,建议生成之后设置 false 
      filepath: "./.eslintrc-auto-import.json", // 指定自动导入函数 eslint 规则的文件
    },
    dts: path.resolve(pathSrc, "types", "auto-imports.d.ts"), // 指定自动导入函数TS类型声明文件路径
  }),
  Components({
    dts: path.resolve(pathSrc, "types", "components.d.ts"), // 指定自动导入组件TS类型声明文件路径
  }),
]

 .eslintrc.cjs - 自动导入函数 eslint 规则引入

"extends": [
    "./.eslintrc-auto-import.json"
],

tsconfig.json - 自动导入TS类型声明文件引入

{
  "include": ["src/**/*.d.ts"]
}

 自动导入效果

 1.5 整合 Element Plus

安装 Element Plus

npm install element-plus

安装自动导入 Icon 依赖(有两种方法)

npm i -D unplugin-icons

第一种方法:vite.config.ts 配置

// vite.config.ts
import vue from "@vitejs/plugin-vue";
import { UserConfig, ConfigEnv, loadEnv, defineConfig } from "vite";

import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import Icons from "unplugin-icons/vite";
import IconsResolver from "unplugin-icons/resolver";

export default ({ mode }: ConfigEnv): UserConfig => {

  return {
    plugins: [
      // ...
      AutoImport({
        // ...  
        resolvers: [
          // 自动导入 Element Plus 相关函数,如:ElMessage, ElMessageBox... (带样式)
          ElementPlusResolver(),
          // 自动导入图标组件
          IconsResolver({}),
        ]
        vueTemplate: true, // 是否在 vue 模板中自动导入
        dts: path.resolve(pathSrc, 'types', 'auto-imports.d.ts') // 自动导入组件类型声明文件位置,默认根目录
          
      }),
      Components({ 
        resolvers: [
          // 自动导入 Element Plus 组件
          ElementPlusResolver(),
          // 自动注册图标组件
          IconsResolver({
            enabledCollections: ["ep"] // element-plus图标库,其他图标库 https://icon-sets.iconify.design/
          }),
        ],
        dts: path.resolve(pathSrc, "types", "components.d.ts"), //  自动导入组件类型声明文件位置,默认根目录
      }),
      Icons({
        // 自动安装图标库
        autoInstall: true,
      }),
    ],
  };
};

示例代码

<!-- src/components/HelloWorld.vue -->
<div>
  <el-button type="success"><i-ep-SuccessFilled />Success</el-button>
  <el-button type="info"><i-ep-InfoFilled />Info</el-button>
  <el-button type="warning"><i-ep-WarningFilled />Warning</el-button>
  <el-button type="danger"><i-ep-WarnTriangleFilled />Danger</el-button>
</div>

第二种方法:main.ts

// 引入ElementPlus所有图标
import * as ElementPlusIconsVue from "@element-plus/icons-vue";


// 注册ElementPlus所有图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
  app.component(key, component);
}

 国际化

main.ts

// 引入ElementPlus
import ElementPlus from 'element-plus'
// 引入ElementPlus的css
import 'element-plus/dist/index.css'
// @ts-expect-error忽略当前文件ts类型的检测否则有红色提示(打包会失败)
import zhCn from "element-plus/dist/locale/zh-cn.mjs";


// 注册ElementPlus
app.use(ElementPlus, {
  locale: zhCn
})

app.vue

import zhCn from 'element-plus/es/locale/lang/zh-cn'


  <el-config-provider :locale="zhCn">
      <el-date-picker v-model="value1" type="date" />
    </el-config-provider>

1.6 整合 SVG 图标

安装依赖

npm install -D fast-glob@3.2.11 vite-plugin-svg-icons@2.0.1 

创建 src/assets/icons 目录 , 放入从 Iconfont 复制的 svg 图标

main.ts 引入注册脚本

// src/main.ts
import 'virtual:svg-icons-register';

 vite.config.ts 配置插件

// vite.config.ts
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';

export default ({command, mode}: ConfigEnv): UserConfig => {
 return (
     {
         plugins: [
             createSvgIconsPlugin({
                 // 指定需要缓存的图标文件夹
                 iconDirs: [path.resolve(process.cwd(), 'src/assets/icons')],
                 // 指定symbolId格式
                 symbolId: 'icon-[dir]-[name]',
             })
         ]
     }
 )
}

SVG 组件封装

<!-- src/components/SvgIcon/index.vue -->
<script setup lang="ts">
const props = defineProps({
  prefix: {
    type: String,
    default: "icon",
  },
  iconClass: {
    type: String,
    required: false,
  },
  color: {
    type: String,
  },
  size: {
    type: String,
    default: "1em",
  },
});

const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
</script>

<template>
  <svg
    aria-hidden="true"
    class="svg-icon"
    :style="'width:' + size + ';height:' + size"
  >
    <use :xlink:href="symbolId" :fill="color" />
  </svg>
</template>

<style scoped>
.svg-icon {
  display: inline-block;
  outline: none;
  width: 1em;
  height: 1em;
  vertical-align: -0.15em; /* 因icon大小被设置为和字体大小一致,而span等标签的下边缘会和字体的基线对齐,故需设置一个往下的偏移比例,来纠正视觉上的未对齐效果 */
  fill: currentColor; /* 定义元素的颜色,currentColor是一个变量,这个变量的值就表示当前元素的color值,如果当前元素未设置color值,则从父元素继承 */
  overflow: hidden;
}
</style>

组件使用

<!-- src/components/HelloWorld.vue -->
<template>
 <el-button type="info"><svg-icon icon-class="block"/>SVG 本地图标</el-button>
</template>

1.7 整合 SCSS

安装依赖

npm i -D sass 

创建 variables.scss 变量文件,添加变量 $bg-color 定义,注意规范变量以 $ 开头

// src/styles/variables.scss
$bg-color:#242424;

Vite 配置导入 SCSS 全局变量文件

// vite.config.ts

  // 路径别名
    resolve: {
      alias: {
        '@': pathSrc
      }
    },

    css: {
      // CSS 预处理器
      preprocessorOptions: {
        //define global scss variable
        scss: {
          javascriptEnabled: true,
          additionalData: `@use "@/styles/variables.scss" as *;`
        }
      }
    }

style 标签使用SCSS全局变量

<!-- src/components/HelloWorld.vue -->
<template>
  <div class="box" />
</template>

<style lang="scss" scoped>
.box {
  width: 100px;
  height: 100px;
  background-color: $bg-color;
}
</style>

上面导入的 SCSS 全局变量在 TypeScript 不生效的,需要创建一个以 .module.scss 结尾的文件

// src/styles/variables.module.scss

// 导出 variables.scss 文件的变量
:export{
    bgColor:$bg-color
}

TypeScript 使用 SCSS 全局变量

<!-- src/components/HelloWorld.vue -->
<script setup lang="ts">
  import variables from "@/styles/variables.module.scss";
  console.log(variables.bgColor)  
</script>

<template>
  <div style="width:100px;height:100px" :style="{ 'background-color': variables.bgColor }" />
</template>

1.8 整合 UnoCSS

安装依赖

npm install -D unocss

vite.config.ts 配置 

// vite.config.ts
import UnoCSS from 'unocss/vite'

export default {
  plugins: [
    UnoCSS({ /* options */ }),
  ],
}

main.ts 引入 uno.css

// src/main.ts
import 'uno.css'

VSCode 安装 UnoCSS 插件

 1.9 整合 Pinia

 安装依赖

npm install pinia
//持久化
npm i pinia-plugin-persistedstate

创建 stores/index.ts

// 创建大仓库
import { createPinia } from 'pinia'
// pinia持久化
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
// createPinia方法可以用于创建大仓库
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
// 对外暴露,安装仓库
export default pinia

main.ts引入

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import 'virtual:svg-icons-register'
import 'uno.css'

// 引入仓库pinia
import pinia from './stores/index.ts'

// 创建app
const app = createApp(App)

// 注册pinia
app.use(pinia)
// 挂载
app.mount('#app')

创建 stores/modules/counter.ts 


import { defineStore } from 'pinia'
// defineStore方法执行会返回一个函数,函数的作用就是让组件可以获取到仓库数据
const counterStore = defineStore('counter', {
  // 开启数据持久化
  persist: {
    // enabled: true, // true 表示开启持久化保存,默认localStorage
    key: 'counter', // 默认会以 store 的 id 作为 key
    storage: localStorage
  },
  // 可以通过为属性指定选项来配置持久化方式persist
  // persist: {
  //     paths: ['isCollapse'],
  //     storage: sessionStorage
  // },
  // 存储数据state
  state: (): any => {
    return {
      count: 5
    }
  },
  // 该函数没有上下文数据,所以获取state中的变量需要使用this
  actions: {
    increment() {
      this.count++
      console.log(this.count)
    }
  },
  // 计算属性,和vuex是使用一样,getters里面不是方法,是计算返回的结果值
  getters: {
    double(state) {
      return state.count * 2
    }
  }
})

// 对外暴露方法
export default counterStore

在App.vue引入试用

import useCounterStore from '@/stores/modules/counter.ts'
const counterStore = useCounterStore()


 <el-button type="primary" @click="counterStore.increment">count++</el-button>

2.0 环境变量

 参考: Vite 环境变量配置官方文档

env配置文件

项目根目录新建 .env.development 、.env.production

环境变量智能提示

新建 src/types/env.d.ts文件存放环境变量TS类型声明 

// src/types/env.d.ts
interface ImportMetaEnv {
  /**
   * 应用标题
   */
  VITE_APP_TITLE: string;
  /**
   * 应用端口
   */
  VITE_APP_PORT: number;
  /**
   * API基础路径(反向代理)
   */
  VITE_APP_BASE_API: string;
 /**
   * 接口地址
   */
  VITE_APP_API_URL: string;
}

interface ImportMeta {
  readonly env: ImportMetaEnv;
}

使用自定义环境变量就会有智能提示,环境变量的读取和使用请看下一节的跨域处理中的 vite.config.ts的配置。

2.1 反向代理解决跨域

跨域原理

vite.config.ts 配置代理

// 路径别名
    resolve: {
      alias: {
        '@': pathSrc
      }
    },
    server: {
      // 允许IP访问
      host: "0.0.0.0",
      // 应用端口 (默认:3000)
      port: Number(env.VITE_APP_PORT),
      // 运行是否自动打开浏览器
      open: true,
      proxy: {
        /** 代理前缀为 /dev-api 的请求  */
        [env.VITE_APP_BASE_API]: {
          changeOrigin: true,
          // 接口地址
          target: env.VITE_APP_API_URL,
          rewrite: (path) =>
            path.replace(new RegExp("^" + env.VITE_APP_BASE_API), ""),
        },
      },
    },

根目录nginx.conf


user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log notice;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    #gzip  on;

    include /etc/nginx/conf.d/*.conf;

    server {
        listen     3000;
        server_name  localhost;

        location / {
            root /usr/share/nginx/html;
            index index.html index.htm;
        }

        # 代理转发请求至网关,prod-api标识解决跨域,vapi.youlai.tech 线上接口地址,注意后面/
        location /prod-api/ {
          proxy_pass https://web-test.digitmold.com:9901/;
        }

         # 代理转发请求至网关,prod-api标识解决跨域,vapi.youlai.tech 线上接口地址,注意后面/
        location /prod-dme-api/ {
          proxy_pass https://web-test2.digitmold.com:9901/;
        }
    }

}

2.2 整合 Axios

npm install axios

Axios 工具类封装

//  src/utils/request.ts


import axios, { InternalAxiosRequestConfig, AxiosResponse, AxiosRequestConfig } from 'axios'
import { koiMsgError } from '@/utils/koi.ts'
import { LOGIN_URL } from '@/config/index.ts'
import useUserStore from '@/stores/modules/user.ts'
import { getToken } from '@/utils/storage.ts'
import router from '@/routers/index.ts'

// 创建 axios 实例
const request = axios.create({
  baseURL: import.meta.env.VITE_APP_BASE_API,
  timeout: 50000,
  headers: { 'Content-Type': 'application/json;charset=utf-8' }
})

// 请求拦截器
request.interceptors.request.use(
  (config: InternalAxiosRequestConfig) => {
    // 获取token
    const token = getToken()
    // 如果实现挤下线功能,需要用户绑定一个uuid,uuid发生变化,后端将数据进行处理[直接使用Sa-Token框架也阔以]
    if (token) {
      config.headers!['Authorization'] = 'Bearer ' + token
    }
    return config
  },
  (error: any) => {
    error.data = {}
    error.data.msg = '服务器异常,请联系管理员🌻'
    return error
  }
)

// 响应拦截器
request.interceptors.response.use(
  (res: AxiosResponse) => {
    // console.log("axios返回数据:", res);
    // console.log("服务器状态",res.status);
    const status = res.data.status || res.data.code // 后端返回数据状态
    if (status == 200) {
      // 服务器连接状态,非后端返回的status 或者 code
      // 这里的后端可能是code OR status 和 msg OR message需要看后端传递的是什么?
      // console.log("200状态", status);
      return res.data
    } else if (status == 401) {
      // console.log("401状态", status);
      const userStore = useUserStore()
      userStore.setToken('') // 清空token必须使用这个,不能使用session清空,因为登录的时候js会获取一遍token还会存在。
      koiMsgError('登录身份过期,请重新登录🌻')
      router.replace(LOGIN_URL)
      return Promise.reject(res.data)
    } else {
      // console.log("后端返回数据:",res.data.msg)
      koiMsgError(res.data.msg + '🌻' || '服务器偷偷跑到火星去玩了🌻')
      return Promise.reject(res.data.msg + '🌻' || '服务器偷偷跑到火星去玩了🌻') // 可以将异常信息延续到页面中处理,使用try{}catch(error){};
    }
  },
  (error: any) => {
    // 处理网络错误,不是服务器响应的数据
    // console.log("进入错误",error);
    error.data = {}
    if (error && error.response) {
      switch (error.response.status) {
        case 400:
          error.data.msg = '错误请求🌻'
          koiMsgError(error.data.msg)
          break
        case 401:
          error.data.msg = '未授权,请重新登录🌻'
          koiMsgError(error.data.msg)
          break
        case 403:
          error.data.msg = '对不起,您没有权限访问🌻'
          koiMsgError(error.data.msg)
          break
        case 404:
          error.data.msg = '请求错误,未找到请求路径🌻'
          koiMsgError(error.data.msg)
          break
        case 405:
          error.data.msg = '请求方法未允许🌻'
          koiMsgError(error.data.msg)
          break
        case 408:
          error.data.msg = '请求超时🌻'
          koiMsgError(error.data.msg)
          break
        case 500:
          error.data.msg = '服务器又偷懒了,请重试🌻'
          koiMsgError(error.data.msg)
          break
        case 501:
          error.data.msg = '网络未实现🌻'
          koiMsgError(error.data.msg)
          break
        case 502:
          error.data.msg = '网络错误🌻'
          koiMsgError(error.data.msg)
          break
        case 503:
          error.data.msg = '服务不可用🌻'
          koiMsgError(error.data.msg)
          break
        case 504:
          error.data.msg = '网络超时🌻'
          koiMsgError(error.data.msg)
          break
        case 505:
          error.data.msg = 'http版本不支持该请求🌻'
          koiMsgError(error.data.msg)
          break
        default:
          error.data.msg = `连接错误${error.response.status}`
          koiMsgError(error.data.msg)
      }
    } else {
      error.data.msg = '连接到服务器失败🌻'
      koiMsgError(error.data.msg)
    }
    return Promise.reject(error) // 将错误返回给 try{} catch(){} 中进行捕获,就算不进行捕获,上方 res.data.status != 200也会抛出提示。
  }
)

// 导出 axios 实例
export default async <T = any>(config: AxiosRequestConfig) => {
  const res = await request(config)
  return (res?.data || res?.data?.data) as T
}

2.3 vue-router 动态路由

安装 vue-router

npm install vue-router@next

路由实例

创建路由实例,顺带初始化静态路由,而动态路由需要用户登录,根据用户拥有的角色进行权限校验后进行初始化

具体代码看文件

// src/routers/index.ts

import { createRouter, createWebHashHistory, createWebHistory } from "vue-router";
import { layoutRouter, staticRouter, errorRouter } from "@/routers/modules/staticRouter";
import { RouteLocationNormalized, NavigationGuardNext } from "vue-router";
import useUserStore from "@/stores/modules/user.ts";
// import useAuthStore from "@/stores/modules/auth.ts";
import { LOGIN_URL, ROUTER_WHITE_LIST } from "@/config/index.ts";
import { koiMsgWarning } from "@/utils/koi.ts";
import { initDynamicRouter } from "@/routers/modules/dynamicRouter.ts";
import NProgress from "nprogress";
import "nprogress/nprogress.css";

NProgress.configure({
  easing: "ease", // 动画方式
  speed: 500, // 递增进度条的速度
  showSpinner: true, // 是否显示加载ico
  trickleSpeed: 200, // 自动递增间隔
  minimum: 0.3 // 初始化时的最小百分比
});
// .env配置文件读取
const mode = import.meta.env.VITE_ROUTER_MODE;

// 路由访问两种模式:带#号的哈希模式,正常路径的web模式。
const routerMode: any = {
  hash: () => createWebHashHistory(),
  history: () => createWebHistory()
};

// 创建路由器对象
const router = createRouter({
  // 路由模式hash或者默认不带#
  history: routerMode[mode](),
  routes: [...layoutRouter, ...staticRouter, ...errorRouter],
  strict: false,
  // 滚动行为
  scrollBehavior() {
    return {
      left: 0,
      top: 0
    };
  }
});

/**
 * @description 前置路由
 * */
router.beforeEach(async (to: RouteLocationNormalized, from: RouteLocationNormalized, next: NavigationGuardNext) => {
  const userStore = useUserStore();
  // const authStore = useAuthStore();

  // 1、NProgress 开始
  NProgress.start();

  // 2、标题切换,没有防止后置路由,是因为页面路径不存在,title会变成undefined
  const title = import.meta.env.VITE_WEB_TITLE;
  document.title = to.meta.title || title;
  console.log(to.path.toLocaleLowerCase());

  // 3、判断是访问登陆页,有Token访问当前页面,token过期访问接口,axios封装则自动跳转登录页面,没有Token重置路由到登陆页。
  if (to.path.toLocaleLowerCase() === LOGIN_URL) {
    // 有Token访问当前页面
    if (userStore.token) {
      return next(from.fullPath);
    } else {
      koiMsgWarning("账号身份已过期,请重新登录🌻");
    }
    // 没有Token重置路由到登陆页。
    resetRouter();
    return next();
  }

  // 4、判断访问页面是否在路由白名单地址[静态路由]中,如果存在直接放行。
  if (ROUTER_WHITE_LIST.includes(to.path)) return next();

  // 5、判断是否有 Token,没有重定向到 login 页面。
  if (!userStore.token) return next({ path: LOGIN_URL, replace: true });

  // 6、如果没有菜单列表[一级扁平化路由 OR 递归菜单路由数据判断是否存在都阔以],就重新请求菜单列表并添加动态路由。
  // if (!authStore.getMenuList.length) {
  //   // 注意:authStore.getMenuList,不能持久化菜单数据,否则这里一直有值,就不会走这里,而且持久化之后还会被篡改数据。
  //   // 获取相关菜单数据 && 按钮数据 && 角色数据 && 用户信息。
  //   // console.log("刷新页面");
  //   await initDynamicRouter();
  //   return next({ ...to, replace: true }); // ...to 保证路由添加完了再进入页面 (可以理解为重进一次) replace: true 重进一次, 不保留重复历史
  // }
  // 7、正常访问页面。
  next();
});

/**
 * @description 重置路由
 */
export const resetRouter = () => {
  // const authStore = useAuthStore();
  // authStore.getMenuList.forEach((route: any) => {
  //   const { name } = route;
  //   if (name && router.hasRoute(name)) {
  //     router.removeRoute(name);
  //   }
  // });
};

/**
 * @description 路由跳转错误
 */
router.onError(error => {
  // 结束全屏动画
  NProgress.done();
  console.warn("路由错误", error.message);
});

/**
 * @description 后置路由
 */
// @ts-expect-error
router.afterEach((to: RouteLocationNormalized, from: RouteLocationNormalized) => {
  // console.log("后置守卫", to, from);
  // 结束全屏动画
  NProgress.done();
});

export default router;

2.4 代码统一规范

2.5 Git 提交规范 

举报

相关推荐

0 条评论