前端项目初始化搭建(二)

news2024/12/19 1:36:06

一、使用 Vite 创建 Vue 3 + TypeScript 项目

PS E:\web\cursor-project\web> npm create vite@latest yf-blog -- --template vue-ts

> npx
> create-vite yf-blog --template vue-ts


Scaffolding project in E:\web\cursor-project\web\yf-blog...

Done. Now run:

  cd yf-blog
  npm install
  npm run dev

PS E:\web\cursor-project\web> cd yf-blog
PS E:\web\cursor-project\web\yf-blog> npm install

added 47 packages in 7s

5 packages are looking for funding
  run `npm fund` for details
PS E:\web\cursor-project\web\yf-blog> npm run dev

> yf-blog@0.0.0 dev
> vite


  VITE v6.0.3  ready in 594 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

在这里插入图片描述

二、安装生产必要依赖

PS E:\web\cursor-project\web\yf-blog> npm install vue-router@4 pinia element-plus @element-plus/icons-vue axios marked highlight.js

added 16 packages in 4s

9 packages are looking for funding
  run `npm fund` for details

三、安装开发依赖

PS E:\web\cursor-project\web\yf-blog> npm install -D sass sass-loader mockjs @types/mockjs vite-plugin-mock cross-env unplugin-auto-import unplugin-vue-components

added 87 packages in 8s

26 packages are looking for funding
  run `npm fund` for details

四、配置别名

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
// https://vite.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    }
  },
  plugins: [vue()],
})

在src下的vite-env.d.ts文件增加模块定义,否则别名引用会报错找不到模块

npm i @types/node --D
declare module "*.vue" {
  import type { DefineComponent } from "vue";
  const component: DefineComponent<typeof DefineComponent>;
  export default component;
}

在tsconfig.app.json添加

{
  "compilerOptions": {
  	"paths": {
      "@": ["./src"]
    }
  },
  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
}

五、依赖的功能和使用方法

1. vue-router@4

功能: Vue.js的官方路由管理器
安装: npm install vue-router@4

使用步骤:

1、新建src/router/index.ts

import { createRouter, createWebHistory } from 'vue-router'

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      name: 'Home',
      component: () => import('@/views/Home.vue')
    },
    {
      path: '/article/:id',
      name: 'Article',
      component: () => import('@/views/Article.vue')
    }
  ]
})

export default router

2、main.ts引入router

import router from '@/router/index'
const app = createApp(App)
app.use(router)
app.mount('#app')

3、在组件中使用:

<script setup lang="ts">
const router = useRouter()
const route = useRoute()

// 编程式导航
const goToArticle = (id: number) => {
  router.push(`/article/${id}`)
}

// 获取路由参数
const articleId = route.params.id
</script>

2. element-plus

功能: 基于 Vue 3的UI组件库
安装: npm install element-plus

使用步骤:

1、main.ts引入 element-plus

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from '@/router/index'
import ElementPlus from 'element-plus'

// 样式导入
import 'element-plus/dist/index.css'
// 创建应用实例
const app = createApp(App)

app.use(router)
app.use(ElementPlus, {
    size: 'default',
    zIndex: 3000
})
app.mount('#app')

2、路由layout组件化处理

在这里插入图片描述

3、新建layout模块

在这里插入图片描述

3.1、src/layout/index.vue内容如下

<template>
    <el-container class="common-layout">
        <el-aside class="aside">
            <Aside/>
        </el-aside>
        <el-container>
            <el-header class="header">
                <Header/>
            </el-header>
            <el-main>
                <router-view></router-view>
            </el-main>
            <el-footer class="footer">Footer</el-footer>
        </el-container>
    </el-container>
</template>

<script setup lang="ts">
import Aside from './components/aside.vue'
import Header from './components/header.vue'

</script>

<style lang="scss" scoped>
.common-layout{
    width: 100%;
    height: 100vh;
    .aside{
		height: 100vh;
        width: 200px;
		background-color: #ccc;
	}
	.header{
        height: 50px;
        background-color: #c9c1c1;
        border-bottom: 1px solid #c9c6c6;
	}
    .footer{
        height: 50px;
        background-color: #c9c1c1;
    }
}
	
</style>

3.2、aside.vue、header.vue、home.vue内容相似如下

<template>
    <div class="home">
        <span>侧边/头部/博客首页</span>
    </div>
</template>
  
<script setup lang="ts">
    
</script>
  
<style lang="scss" scoped>

</style> 

最终页面效果如下

在这里插入图片描述

3. element-plus/icons-vue

功能: Element Plus的图标库
安装: npm install @element-plus/icons-vue

使用步骤:

1、main.ts引入 element-plus/icons-vue

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from '@/router/index'
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'

// 样式导入
import 'element-plus/dist/index.css'
// 创建应用实例
const app = createApp(App)
// 注册 Element Plus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
}
app.use(router)
app.use(ElementPlus, {
    size: 'default',
    zIndex: 3000
})
app.mount('#app')

2、在Home.vue组件中应用

<template>
    <div class="home">
        <h1>博客首页</h1>
        <el-icon :size="50"><House /></el-icon>
    </div>
</template>
  
<script setup lang="ts">
    
</script>
  
<style lang="scss" scoped>

</style> 

在这里插入图片描述

4. unplugin-auto-import

功能: 自动导入API插件
安装: npm install -D unplugin-auto-import

使用步骤:

1、在vite.config.ts文件中添加配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import AutoImport from 'unplugin-auto-import/vite'
// https://vite.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    }
  },
  plugins: [
    vue(),
    AutoImport({
      imports:["vue","vue-router"],
      dts:'src/auto-import.d.ts',  // 路径下自动生成文件夹存放全局指令 
      eslintrc: {
        enabled: true,  // 1、改为true用于生成eslint配置。2、生成后改回false,避免重复生成消耗
      } 
    })
  ],
})

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
这里ref正常使用,并没有报错,说明已经自动导入完成

5. unplugin-auto-import

功能: 在Vue文件中自动引入组件
安装: npm install -D unplugin-vue-components

使用步骤:

1、在vite.config.ts文件中添加配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src')
    }
  },
  plugins: [
    vue(),
    AutoImport({
      imports:["vue","vue-router"],
      dts:'src/auto-import.d.ts',  // 路径下自动生成文件夹存放全局指令 
      eslintrc: {
        enabled: false,  // 1、改为true用于生成eslint配置。2、生成后改回false,避免重复生成消耗
      },
      resolvers: [ElementPlusResolver()] 
    }),
    Components({
      dirs: ['src/components'], // 配置需要默认导入的自定义组件文件夹,该文件夹下的所有组件都会自动 import
      resolvers: [ElementPlusResolver()],
    }),
  ],
})

1、在home.vue组件中使用

<template>
    <div class="home">
        <h1>{{ title }}</h1>
        <!-- <el-icon :size="50"><House /></el-icon> -->
        <el-button type="primary">点击</el-button>
    </div>
</template>
  
<script setup lang="ts">
    const title = ref('首页')
</script>
  
<style lang="scss" scoped>

</style> 

在这里插入图片描述
这里直接使用,样式效果都正常显示,证明引入成功。

6. Pinia

功能: Vue 3的状态管理库
安装: npm install pinia pinia-plugin-persistedstate

使用步骤:

1、创建src/stores/index.ts

import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
 
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
 
export default pinia

2、main.ts引入 Pinia

import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import router from '@/router/index'
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import store from './stores/index'
// 样式导入
import 'element-plus/dist/index.css'
// 创建应用实例
const app = createApp(App)
// 注册 Element Plus 图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
    app.component(key, component)
}
app.use(router)
app.use(ElementPlus, {
    size: 'default',
    zIndex: 3000
})
app.use(store)
app.mount('#app')

3、创建src/stores/article.ts

import { defineStore } from 'pinia'
interface Article {
id: number
title: string
content: string
}
export const useArticleStore = defineStore('article', {
  state: () => ({
    articles: [] as Article[],
    currentArticle:null as Article | null
  }),
  actions: {
    fetchArticles() {
      this.articles = [{
        id: 1,
        title: '文章1',
        content: '文章内容1'
      }]
    }
  },
  getters: {
    getArticleById: (state) => (id: number) => {
      return state.articles.find(article => article.id === id)
    }
  },
  persist: true,//持久化存储
})

4、在组件中应用

<template>
    <div class="home">
        <h1>{{ title }}</h1>
        <!-- <el-icon :size="50"><House /></el-icon> -->
        <!-- <el-button type="primary">点击</el-button> -->
        <div v-for="article in articles" :key="article.id">
            <h2>{{ article.title }}</h2>
            <p>{{ article.content }}</p>
        </div>
    </div>
</template>
  
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useArticleStore } from '@/stores/article'
const articleStore = useArticleStore()
const { articles } = storeToRefs(articleStore)
const title = ref('首页')
onMounted(() => {
    articleStore.fetchArticles()
})
</script>
  
<style lang="scss" scoped>

</style> 

在这里插入图片描述

7. axios

功能: 基于Promise的HTTP客户端
安装: npm install axios

使用步骤:

开发前提需要配置代理

server: {
  host: '0.0.0.0',
  port: port,
  open: true,
  proxy: {
    [VITE_APP_BASE_API]: {
      target: VITE_SERVE,
      changeOrigin: true,
      rewrite: path => path.replace(RegExp(`^${VITE_APP_BASE_API}`), '')
    }
  },
  disableHostCheck: true
},

1、创建src/utils/request.ts

import axios, { AxiosRequestConfig } from 'axios'
import { ElNotification , ElMessageBox, ElMessage, ElLoading, LoadingParentElement } from 'element-plus'
import { getToken } from '@/utils/auth'
import { tansParams, blobValidate } from '@/utils/mis'
import cache from '@/plugins/cache'
import { saveAs } from 'file-saver'
import useUserStore from '@/stores/modules/user'
import { ComponentOptionsBase, ComponentProvideOptions } from 'vue'

var downloadLoadingInstance: { close: any; setText?: (text: string) => void; removeElLoadingChild?: () => void; handleAfterLeave?: () => void; vm?: globalThis.ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase<any, any, any, any, any, any, any, any, any, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions>, {}, {}, "", {}, any>; $el?: HTMLElement; originalPosition?: globalThis.Ref<string, string>; originalOverflow?: globalThis.Ref<string, string>; visible?: globalThis.Ref<boolean, boolean>; parent?: globalThis.Ref<LoadingParentElement, LoadingParentElement>; background?: globalThis.Ref<string, string>; svg?: globalThis.Ref<string, string>; svgViewBox?: globalThis.Ref<string, string>; spinner?: globalThis.Ref<string | boolean, string | boolean>; text?: globalThis.Ref<string, string>; fullscreen?: globalThis.Ref<boolean, boolean>; lock?: globalThis.Ref<boolean, boolean>; customClass?: globalThis.Ref<string, string>; target?: globalThis.Ref<HTMLElement, HTMLElement>; beforeClose?: globalThis.Ref<(() => boolean) | undefined, (() => boolean) | undefined> | undefined; closed?: globalThis.Ref<(() => void) | undefined, (() => void) | undefined> | undefined };
let errorCode = {
    '401': '登录状态已过期,您可以继续留在该页面,或者重新登录',
    '403': '当前操作没有权限',
    '404': '请求的资源不存在',
    '500': '服务器错误',
    '601': '请求参数错误',
    'default': '请求失败,请稍后再试'
}
// 是否显示重新登录
export let isRelogin = { show: false };

axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
  // axios中请求配置有baseURL选项,表示请求URL公共部分
  baseURL: import.meta.env.VITE_APP_BASE_API,
  // 超时
  timeout: 60000
})

// request拦截器
service.interceptors.request.use(config => {
  // 是否需要设置 token
  const isToken = (config.headers || {}).isToken === false
  // 是否需要防止数据重复提交
  const isRepeatSubmit = (config.headers || {}).repeatSubmit === false
  if (getToken() && !isToken) {
    config.headers['Authorization'] = 'Bearer ' + getToken() // 让每个请求携带自定义token 请根据实际情况自行修改
  }
  if(config.headers){
    config.headers['Content-Type'] = config.headers['Content-Type']
  }
  // get请求映射params参数
  if (config.method === 'get' && config.params) {
    let url = config.url + '?' + tansParams(config.params);
    url = url.slice(0, -1);
    config.params = {};
    config.url = url;
  }
  if (!isRepeatSubmit && (config.method === 'post' || config.method === 'put'|| config.method === 'delete')) {
    const requestObj = {
      url: config.url,
      data: typeof config.data === 'object' ? JSON.stringify(config.data) : config.data,
      time: new Date().getTime()
    }
    const requestSize = Object.keys(JSON.stringify(requestObj)).length; // 请求数据大小
    const limitSize = 5 * 1024 * 1024; // 限制存放数据5M
    if (requestSize >= limitSize) {
      console.warn(`[${config.url}]: ` + '请求数据大小超出允许的5M限制,无法进行防重复提交验证。')
      return config;
    }
    const sessionObj = cache.session.getJSON('sessionObj')
    
    if (sessionObj === undefined || sessionObj === null || sessionObj === '') {
      cache.session.setJSON('sessionObj', requestObj as any)
    } else {
      const s_url = sessionObj.url;                // 请求地址
      const s_data = sessionObj.data;              // 请求数据
      const s_time = sessionObj.time;              // 请求时间
      const interval = 100;                       // 间隔时间(ms),小于此时间视为重复提交
      if (s_data === requestObj.data && s_url === requestObj.url&& requestObj.time - s_time < interval) {
        const message = '数据正在处理,请勿重复提交';
        console.warn(`[${s_url}]: ` + message)
        return Promise.reject(new Error(message))
      } else {
        cache.session.setJSON('sessionObj', requestObj as any)
      }
    }
  }
  return config
}, error => {
    console.log(error)
    Promise.reject(error)
})

// 响应拦截器
service.interceptors.response.use(res => {
    // 未设置状态码则默认成功状态
    const code = res.data.code || 200;
    // 获取错误信息
    const msg = errorCode[code as keyof typeof errorCode] || res.data.msg || errorCode['default']
    // 二进制数据则直接返回
    if (res.request.responseType ===  'blob' || res.request.responseType ===  'arraybuffer') {
      return res.data
    }
    if (code === 401) {
      if (!isRelogin.show) {
        isRelogin.show = true;
        ElMessageBox.confirm('登录状态已过期,您可以继续留在该页面,或者重新登录', '系统提示', { confirmButtonText: '重新登录', cancelButtonText: '取消', type: 'warning' }).then(() => {
            isRelogin.show = false;
            useUserStore().logOut().then(() => {
              location.href = '/index';
            })
        }).catch(() => {
          isRelogin.show = false;
        });
      }
      return Promise.reject('无效的会话,或者会话已过期,请重新登录。')
    } else if (code === 500) {
      ElMessage({ message: msg, type: 'error' })
      return Promise.reject(new Error(msg))
    } else if (code === 601) {
      ElMessage({ message: msg, type: 'warning' })
      return Promise.reject(new Error(msg))
    } else if (code !== 200&& code !== 400) {
      ElNotification.error({ title: msg })
      return Promise.reject('error')
    } else {
      return  Promise.resolve(res.data)
    } 
  },
  error => {
    console.log('err' + error)
    let { message } = error;
    if (message == "Network Error") {
      message = "后端接口连接异常";
    } else if (message.includes("timeout")) {
      message = "系统接口请求超时";
    } else if (message.includes("Request failed with status code")) {
      message = "系统接口" + message.substr(message.length - 3) + "异常";
    }
    ElMessage({ message: message, type: 'error', duration: 5 * 1000 })
    return Promise.reject(error)
  }
)

// 通用下载方法
export function download(url: string, params: any, filename: string | undefined, config: AxiosRequestConfig<any> | undefined) {
  downloadLoadingInstance = ElLoading.service({ text: "正在下载数据,请稍候", background: "rgba(0, 0, 0, 0.7)", })
  return service.post(url, params, {
    transformRequest: [(params) => { return tansParams(params) }],
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    responseType: 'blob',
    ...config
  }).then(async (data:any) => {
    const isBlob = blobValidate(data);
    if (isBlob) {
      const blob = new Blob([data])
      saveAs(blob, filename)
    } else {
      const resText = await data.text();
      const rspObj = JSON.parse(resText);
      const errMsg = errorCode[rspObj.code as keyof typeof errorCode] || rspObj.msg || errorCode['default']
      ElMessage.error(errMsg);
    }
    downloadLoadingInstance.close();
  }).catch((r) => {
    console.error(r)
    ElMessage.error('下载文件出现错误,请联系管理员!')
    downloadLoadingInstance.close();
  })
}

export default service

2、创建src/utils/auth.ts

import Cookies from 'js-cookie'

const TokenKey = 'Admin-Token'

export function getToken() {
  return Cookies.get(TokenKey)
}

export function setToken(token:any) {
  return Cookies.set(TokenKey, token,{ expires: 1 })
}

export function removeToken() {
  return Cookies.remove(TokenKey)
}

3、创建src/utils/mis.ts

/**
 * 通用js方法封装处理
 * Copyright (c) 2019 ruoyi
 */

// 日期格式化

  // 日期格式化
// 日期格式化
export function parseTime(time: string | number | Date, pattern: string) {
    if (arguments.length === 0 || !time) {
      return null
    }
    const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
    let date
    if (typeof time === 'object') {
      date = time
    } else {
      if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) {
        time = parseInt(time)
      } else if (typeof time === 'string') {
        time = time.replace(new RegExp(/-/gm), '/').replace('T', ' ').replace(new RegExp(/\.[\d]{3}/gm), '');
      }
      if ((typeof time === 'number') && (time.toString().length === 10)) {
        time = time * 1000
      }
      date = new Date(time)
    }
    const formatObj = {
      y: date.getFullYear(),
      m: date.getMonth() + 1,
      d: date.getDate(),
      h: date.getHours(),
      i: date.getMinutes(),
      s: date.getSeconds(),
      a: date.getDay()
    }
    const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result: string, key: string) => {
        let value = key in formatObj ? formatObj[key as keyof typeof formatObj] : 0;
        // Note: getDay() returns 0 on Sunday
        if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
        if (result.length > 0 && value < 10) {
          return '0' + value.toString(); // 将 value 转换为字符串并返回
        }
        return value.toString(); // 确保返回值是字符串
      });
    return time_str
  }
  
  // 添加日期范围
  export function addDateRange(params: any, dateRange: any[], propName: string) {
    let search = params;
    search.params = typeof (search.params) === 'object' && search.params !== null && !Array.isArray(search.params) ? search.params : {};
    dateRange = Array.isArray(dateRange) ? dateRange : [];
    if (typeof (propName) === 'undefined') {
      search.params['beginTime'] = dateRange[0];
      search.params['endTime'] = dateRange[1];
    } else {
      search.params['begin' + propName] = dateRange[0];
      search.params['end' + propName] = dateRange[1];
    }
    return search;
  }
  
  // 回显数据字典
  export function selectDictLabel(datas: { [x: string]: { label: any, value: any } }, value: string | undefined) {
    if (value === undefined) {
      return "";
    }
    var actions = [];
    Object.keys(datas).some((key) => {
      if (datas[key].value == ('' + value)) {
        actions.push(datas[key].label);
        return true;
      }
    })
    if (actions.length === 0) {
      actions.push(value);
    }
    return actions.join('');
  }
  
  // 回显数据字典(字符串数组)
  export function selectDictLabels(datas: { [key: string]: { label: any, value: any } }, value: string | undefined, separator: undefined) {
    if (value === undefined || value.length ===0) {
      return "";
    }
    if (Array.isArray(value)) {
      value = value.join(",");
    }
    var actions: string[] = [];
    var currentSeparator = undefined === separator ? "," : separator;
    var temp = value.split(currentSeparator);
    Object.keys(value.split(currentSeparator)).forEach((val) => {
      var match = false;
      Object.keys(datas).some((key) => {
        if (datas[key].value == ('' + temp[parseInt(val)])) {
          actions.push(datas[key].label + currentSeparator);
          match = true;
        }
      })
      if (!match) {
        actions.push(temp[parseInt(val)] + currentSeparator);
      }
    })
    return actions.join('').substring(0, actions.join('').length - 1);
  }
  
  // 字符串格式化(%s )
  export function sprintf(str: string) {
    var args = arguments, flag = true, i = 1;
    str = str.replace(/%s/g, function () {
      var arg = args[i++];
      if (typeof arg === 'undefined') {
        flag = false;
        return '';
      }
      return arg;
    });
    return flag ? str : '';
  }
  
  // 转换字符串,undefined,null等转化为""
  export function parseStrEmpty(str: string) {
    if (!str || str == "undefined" || str == "null") {
      return "";
    }
    return str;
  }
  
  // 数据合并
  export function mergeRecursive(source: { [x: string]: any }, target: { [x: string]: any }) {
    for (var p in target) {
      try {
        if (target[p].constructor == Object) {
          source[p] = mergeRecursive(source[p], target[p]);
        } else {
          source[p] = target[p];
        }
      } catch (e) {
        source[p] = target[p];
      }
    }
    return source;
  };
  
  /**
   * 构造树型结构数据
   * @param {*} data 数据源
   * @param {*} id id字段 默认 'id'
   * @param {*} parentId 父节点字段 默认 'parentId'
   * @param {*} children 孩子节点字段 默认 'children'
   */
  export function handleTree(data: any, id: any, parentId: any, children: any) {
    let config = {
      id: id || 'id',
      parentId: parentId || 'parentId',
      childrenList: children || 'children'
    };
  
    const childrenListMap: { [key: string]: any[] } = {};
    const nodeIds: { [key: string]: any } = {};
    var tree = [];
  
    for (let d of data) {
      let parentId = d[config.parentId];
      if (childrenListMap[parentId] == null) {
        childrenListMap[parentId] = [];
      }
      nodeIds[d[config.id]] = d;
      childrenListMap[parentId].push(d);
    }
  
    for (let d of data) {
      let parentId = d[config.parentId];
      if (nodeIds[parentId] == null) {
        tree.push(d);
      }
    }
  
    for (let t of tree) {
      adaptToChildrenList(t);
    }
  
    function adaptToChildrenList(o: { [x: string]: any }) {
      if (childrenListMap[o[config.id]] !== null) {
        o[config.childrenList] = childrenListMap[o[config.id]];
      }
      if (o[config.childrenList]) {
        for (let c of o[config.childrenList]) {
          adaptToChildrenList(c);
        }
      }
    }
    return tree;
  }
  
  /**
  * 参数处理
  * @param {*} params  参数
  */
  export function tansParams(params: { [x: string]: any }) {
    let result = ''
    for (const propName of Object.keys(params)) {
      const value = params[propName];
      var part = encodeURIComponent(propName) + "=";
      if (value !== null && value !== "" && typeof (value) !== "undefined") {
        if (typeof value === 'object') {
          for (const key of Object.keys(value)) {
            if (value[key] !== null && value[key] !== "" && typeof (value[key]) !== 'undefined') {
              let params = propName + '[' + key + ']';
              var subPart = encodeURIComponent(params) + "=";
              result += subPart + encodeURIComponent(value[key]) + "&";
            }
          }
        } else {
          result += part + encodeURIComponent(value) + "&";
        }
      }
    }
    return result
  }
  
  
  // 返回项目路径
  export function getNormalPath(p: string) {
    if (p.length === 0 || !p || p == 'undefined') {
      return p
    };
    let res = p.replace('//', '/')
    if (res[res.length - 1] === '/') {
      return res.slice(0, res.length - 1)
    }
    return res;
  }
  
  // 验证是否为blob格式
  export function blobValidate(data: { type: string }) {
    return data.type !== 'application/json'
  }
  

4、创建src/plugins/cache.ts

const sessionCache = {
    set (key: string | null, value: string | null) {
      if (!sessionStorage) {
        return
      }
      if (key != null && value != null) {
        sessionStorage.setItem(key, value)
      }
    },
    get (key: string | null) {
      if (!sessionStorage) {
        return null
      }
      if (key == null) {
        return null
      }
      return sessionStorage.getItem(key)
    },
    setJSON (key: any, jsonValue: null) {
      if (jsonValue != null) {
        this.set(key, JSON.stringify(jsonValue))
      }
    },
    getJSON (key: any) {
      const value = this.get(key)
      if (value != null) {
        return JSON.parse(value)
      }
    },
    remove (key: string) {
      sessionStorage.removeItem(key);
    }
  }
  const localCache = {
    set (key: string | null, value: string | null) {
      if (!localStorage) {
        return
      }
      if (key != null && value != null) {
        localStorage.setItem(key, value)
      }
    },
    get (key: string | null) {
      if (!localStorage) {
        return null
      }
      if (key == null) {
        return null
      }
      return localStorage.getItem(key)
    },
    setJSON (key: any, jsonValue: null) {
      if (jsonValue != null) {
        this.set(key, JSON.stringify(jsonValue))
      }
    },
    getJSON (key: any) {
      const value = this.get(key)
      if (value != null) {
        return JSON.parse(value)
      }
    },
    remove (key: string) {
      localStorage.removeItem(key);
    }
  }
  
  export default {
    /**
     * 会话级缓存
     */
    session: sessionCache,
    /**
     * 本地缓存
     */
    local: localCache
  }
  

5、创建src/plugins/cache.ts

const sessionCache = {
    set (key: string | null, value: string | null) {
      if (!sessionStorage) {
        return
      }
      if (key != null && value != null) {
        sessionStorage.setItem(key, value)
      }
    },
    get (key: string | null) {
      if (!sessionStorage) {
        return null
      }
      if (key == null) {
        return null
      }
      return sessionStorage.getItem(key)
    },
    setJSON (key: any, jsonValue: null) {
      if (jsonValue != null) {
        this.set(key, JSON.stringify(jsonValue))
      }
    },
    getJSON (key: any) {
      const value = this.get(key)
      if (value != null) {
        return JSON.parse(value)
      }
    },
    remove (key: string) {
      sessionStorage.removeItem(key);
    }
  }
  const localCache = {
    set (key: string | null, value: string | null) {
      if (!localStorage) {
        return
      }
      if (key != null && value != null) {
        localStorage.setItem(key, value)
      }
    },
    get (key: string | null) {
      if (!localStorage) {
        return null
      }
      if (key == null) {
        return null
      }
      return localStorage.getItem(key)
    },
    setJSON (key: any, jsonValue: null) {
      if (jsonValue != null) {
        this.set(key, JSON.stringify(jsonValue))
      }
    },
    getJSON (key: any) {
      const value = this.get(key)
      if (value != null) {
        return JSON.parse(value)
      }
    },
    remove (key: string) {
      localStorage.removeItem(key);
    }
  }
  
  export default {
    /**
     * 会话级缓存
     */
    session: sessionCache,
    /**
     * 本地缓存
     */
    local: localCache
  }
  

6、创建src/stores/modules/user.ts

import { defineStore } from 'pinia'
import { login, logout, getInfo} from '@/api/login'
import { getToken, setToken, removeToken } from '@/utils/auth'
import defAva from '@/assets/vue.svg'

const useUserStore = defineStore(
  'user',
  {
    state: () => ({
      token: getToken(),
      id: '',
      name: '',
      avatar: '',
      roles: [] as string[],
      permissions: [],
      customList: [],
      customer: {}
    }),
    actions: {
      // 登录
      login(userInfo: { username: string }) {
        userInfo.username = userInfo.username.trim()
        return new Promise<void>((resolve, reject) => {
          login(userInfo).then((res: any) => {
            console.log(res,'res111')
            if (res.code === 200) { // 修改这里
              setToken(res.data.access_token)
              this.token = res.data.access_token
              resolve(res)
            } else {
              reject(res.msg) // 修改这里
            }
          }).catch((error: any) => {
            reject(error)
          })
        })
      },
      // 获取用户信息
      getInfo() {
        return new Promise((resolve, reject) => {
          getInfo().then((res: any) => {
            const user = res.data.user // 修改这里
            const avatar = (user.avatar == "" || user.avatar == null) ? defAva : user.avatar;
            if (res.data.roles && res.data.roles.length > 0) { // 修改这里
              this.roles = res.data.roles
              this.permissions = res.data.permissions
            } else {
              this.roles = ['ROLE_DEFAULT']
            }
            this.customer = res.data.customer // 修改这里
            this.id = user.userId
            this.name = user.userName
            this.avatar = avatar
            resolve(res)
          }).catch((error: any) => {
            reject(error)
          })
        })
      },
      // 退出系统
      logOut() {
        return new Promise<void>((resolve, reject) => {
          logout().then(() => {
            this.token = ''
            this.roles = []
            this.permissions = []
            removeToken()
            resolve()
          }).catch((error: any) => {
            reject(error)
          })
        })
      }
    }
  })

export default useUserStore

7、在home.vue中测试应用

<template>
    <div class="home">
        <h1>{{ title }}</h1>
        <!-- <el-icon :size="50"><House /></el-icon> -->
        <!-- <el-button type="primary">点击</el-button> -->
        <div v-for="article in articles" :key="article.id">
            <h2>{{ article.title }}</h2>
            <p>{{ article.content }}</p>
        </div>
    </div>
</template>
  
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useArticleStore } from '@/stores/article'
import useUserStore from '@/stores/modules/user'
const userStore = useUserStore()
const articleStore = useArticleStore()
const { articles } = storeToRefs(articleStore)
const title = ref('首页')

onMounted(() => {
    articleStore.fetchArticles()
    const params = {
        username: "admin",
        password: "******",
    }
    userStore.login(params).then((res) => {
        console.log('res',res)
    }).catch((err) => {
        console.log('err',err)
    });
})
</script>
  
<style lang="scss" scoped>

</style> 

在这里插入图片描述
正常登录成功

8. mockjs & vite-plugin-mock

功能: 模拟接口数据
安装: npm install -D mockjs @types/mockjs vite-plugin-mock

1、在vite.config.ts引入配置mock

import { viteMockServe } from 'vite-plugin-mock'
export default defineConfig(({ mode }) => {
	plugins: [
		viteMockServe({
	        mockPath: './mock/',//设置模拟数据的存储文件夹
	        //@ts-ignore
	        supportTs: true,//是否读取ts文件模块
	        logger:true,//是否在控制台显示请求日志
	        localEnabled: true,//设置是否启用本地mock文件
	        prodEnabled:false//设置打包是否启用mock功能
	    })
	]
})

2、在根目录新建mock/index.ts

import { MockMethod } from 'vite-plugin-mock'
import Mock from 'mockjs'

export default [
  {
    url: '/api/login',
    method: 'post',
    response: () => {
      return {
          code: 200,
          data: Mock.mock({
              access_token: "eyJhbGciOiJIUzUxMiJ9.eyJ1c2VyX2lkIjoxLCJ1c2VyX2tleSI6Ij...",
              customerName: "管理员账户",
              expires_in: 28800,
          }),
          msg:'登录成功'
      }
    }
  }
] as MockMethod[]

在这里插入图片描述
成功返回预设的返回值对象。

9. sass & sass-loader

功能: CSS预处理器
安装: npm install -D sass sass-loader

使用步骤:

1、新建src/assets/styles/index.scss

body {
  --el-color-primary: #4F6EF7;
  height: 100%;
  margin: 0;
  -moz-osx-font-smoothing: grayscale;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  font-family: SourceHanSansSC-Regular, SourceHanSansSC,Helvetica Neue, Helvetica, PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
.el-select__wrapper{
  height: 2.25rem;
}
label {
  font-weight: 700;
}

html {
  height: 100%;
  box-sizing: border-box;
}

#app {
  height: 100%;
}

*,
*:before,
*:after {
  box-sizing: inherit;
}

.no-padding {
  padding: 0px !important;
}

.padding-content {
  padding: 4px 0;
}

a:focus,
a:active {
  outline: none;
}

a,
a:focus,
a:hover {
  cursor: pointer;
  color: inherit;
  text-decoration: none;
}
ul,li{
  list-style: none;
}
div:focus {
  outline: none;
}

.fr {
  float: right;
}

.fl {
  float: left;
}

.pr-5 {
  padding-right: 5px;
}

.pl-5 {
  padding-left: 5px;
}

.block {
  display: block;
}

.pointer {
  cursor: pointer;
}

.inlineBlock {
  display: block;
}

.clearfix {
  &:after {
    visibility: hidden;
    display: block;
    font-size: 0;
    content: " ";
    clear: both;
    height: 0;
  }
}

2、新建src/assets/styles/variables.module.scss

// base color
$blue: #324157;
$light-blue: #3A71A8;
$red: #C03639;
$pink: #E65D6E;
$green: #30B08F;
$tiffany: #4AB7BD;
$yellow: #FEC171;
$panGreen: #30B08F;

// 默认菜单主题风格
$base-menu-color: #bfcbd9;
$base-menu-color-active: #f4f4f5;
$base-menu-background: #304156;
$base-logo-title-color: #ffffff;

$base-menu-light-color: #606787;
$base-menu-light-background: #F2F6FB;
$base-logo-light-title-color: #fff;

$base-sub-menu-background: #1f2d3d;
$base-sub-menu-hover: #001528;

// 自定义暗色菜单风格
/**
$base-menu-color:hsla(0,0%,100%,.65);
$base-menu-color-active:#fff;
$base-menu-background:#001529;
$base-logo-title-color: #ffffff;

$base-menu-light-color:rgba(0,0,0,.70);
$base-menu-light-background:#ffffff;
$base-logo-light-title-color: #001529;

$base-sub-menu-background:#000c17;
$base-sub-menu-hover:#001528;
*/

$--color-primary: #4F6EF7;
$--color-success: #67C23A;
$--color-warning: #E6A23C;
$--color-danger: #F56C6C;
$--color-info: #909399;

$base-sidebar-width: 240px;

// the :export directive is the magic sauce for webpack
// https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass
:export {
  menuColor: $base-menu-color;
  menuLightColor: $base-menu-light-color;
  menuColorActive: $base-menu-color-active;
  menuBackground: $base-menu-background;
  menuLightBackground: $base-menu-light-background;
  subMenuBackground: $base-sub-menu-background;
  subMenuHover: $base-sub-menu-hover;
  sideBarWidth: $base-sidebar-width;
  logoTitleColor: $base-logo-title-color;
  logoLightTitleColor: $base-logo-light-title-color;
  primaryColor: $--color-primary;
  successColor: $--color-success;
  dangerColor: $--color-danger;
  infoColor: $--color-info;
  warningColor: $--color-warning;
}

3、新建src/assets/styles/mixins.scss

@mixin clearfix {
    &:after {
      content: "";
      display: table;
      clear: both;
    }
  }
  
  @mixin scrollBar {
    &::-webkit-scrollbar-track-piece {
      background: #d3dce6;
    }
  
    &::-webkit-scrollbar {
      width: 6px;
    }
  
    &::-webkit-scrollbar-thumb {
      background: #99a9bf;
      border-radius: 20px;
    }
  }
  
  @mixin relative {
    position: relative;
    width: 100%;
    height: 100%;
  }
  
  @mixin pct($pct) {
    width: #{$pct};
    position: relative;
    margin: 0 auto;
  }
  
  @mixin triangle($width, $height, $color, $direction) {
    $width: $width/2;
    $color-border-style: $height solid $color;
    $transparent-border-style: $width solid transparent;
    height: 0;
    width: 0;
  
    @if $direction==up {
      border-bottom: $color-border-style;
      border-left: $transparent-border-style;
      border-right: $transparent-border-style;
    }
  
    @else if $direction==right {
      border-left: $color-border-style;
      border-top: $transparent-border-style;
      border-bottom: $transparent-border-style;
    }
  
    @else if $direction==down {
      border-top: $color-border-style;
      border-left: $transparent-border-style;
      border-right: $transparent-border-style;
    }
  
    @else if $direction==left {
      border-right: $color-border-style;
      border-top: $transparent-border-style;
      border-bottom: $transparent-border-style;
    }
  }
  

4、在home组件中使用

<template>
    <div class="home">
        <h1>{{ title }}</h1>
        <!-- <el-icon :size="50"><House /></el-icon> -->
        <!-- <el-button type="primary">点击</el-button> -->
        <div v-for="article in articles" :key="article.id">
            <h2>{{ article.title }}</h2>
            <p>{{ article.content }}</p>
        </div>
    </div>
</template>
  
<script setup lang="ts">
import { storeToRefs } from 'pinia'
import { useArticleStore } from '@/stores/article'
import useUserStore from '@/stores/modules/user'
const userStore = useUserStore()
const articleStore = useArticleStore()
const { articles } = storeToRefs(articleStore)
const title = ref('首页')

onMounted(() => {
    articleStore.fetchArticles()
    const params = {
        username: "admin",
        password: "******",
    }
    userStore.login(params).then((res) => {
        console.log('res',res)
    }).catch((err) => {
        console.log('err',err)
    });
})
</script>
  
<style lang="scss" scoped>
@import '@/assets/styles/mixins.scss';
.container {
    @include relative;
}
</style> 

在这里插入图片描述
这里可以看出来样式已经生效赋值。

10. cross-env

功能: 跨平台设置环境变量
安装: npm install -D cross-env

使用步骤:
{
  "scripts": {
    "dev": "cross-env NODE_ENV=development vite",
    "build:test": "cross-env NODE_ENV=test vue-tsc && vite build",
    "build:prod": "cross-env NODE_ENV=production vue-tsc && vite build"
  }
}

11. 配置环境变量

使用步骤:

根目录新建.env.development和.env.production

.env.development内容如下:

# 页面标题
VITE_APP_TITLE = '博客'
# 开发环境配置
VITE_APP_ENV = 'development'
# 小麒物联/开发环境
VITE_APP_BASE_API = '/api'
# 是否启用代理
VITE_HTTP_PROXY = true
# 端口
VITE_PORT = 80

# 本地环境接口地址
VITE_SERVE = 'http://******'

.env.production内容如下:

# 页面标题
VITE_APP_TITLE = '博客'
# 开发环境配置
VITE_APP_ENV = 'production'
# 小麒物联/开发环境
VITE_APP_BASE_API = '/api'
# 是否启用代理
VITE_HTTP_PROXY = false
# 端口
VITE_PORT = 80

# 本地环境接口地址
VITE_SERVE = 'http://******'
# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

12. 配置打包文件结构

build: {
  rollupOptions: {
    output: {
      chunkFileNames: 'static/js/[name]-[hash].js',
      entryFileNames: 'static/js/[name]-[hash].js',
      assetFileNames: 'static/[ext]/[name]-[hash][extname]',
      manualChunks(id) {
        if (id.includes('element-plus')) {
          return 'element-plus';
        }
      }
    }
  }
}

完整版vite.config.ts文件

import { defineConfig, loadEnv } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import { viteMockServe } from 'vite-plugin-mock'
// https://vite.dev/config/
export default defineConfig(({ mode }) => {
  const env = loadEnv(mode, process.cwd())
  const { VITE_APP_ENV, VITE_APP_BASE_API, VITE_SERVE, VITE_PORT } = env

  const port = VITE_PORT ? parseInt(VITE_PORT, 10) : 8080

  return {
    base: VITE_APP_ENV === 'production' ? '/' : './',
    resolve: {
      alias: {
        '@': path.resolve(__dirname, './src')
      }
    },
    plugins: [
      vue(),
      AutoImport({
        imports: ["vue", "vue-router"],
        dts: 'src/auto-import.d.ts',  // 路径下自动生成文件夹存放全局指令 
        eslintrc: {
          enabled: false,  // 1、改为true用于生成eslint配置。2、生成后改回false,避免重复生成消耗
        },
        resolvers: [ElementPlusResolver()]
      }),
      Components({
        dirs: ['src/components'], // 配置需要默认导入的自定义组件文件夹,该文件夹下的所有组件都会自动 import
        resolvers: [ElementPlusResolver()],
      }),
      viteMockServe({
        mockPath: './mock/',//设置模拟数据的存储文件夹
        //@ts-ignore
        supportTs: true,//是否读取ts文件模块
        logger:true,//是否在控制台显示请求日志
        localEnabled: true,//设置是否启用本地mock文件
        prodEnabled:false//设置打包是否启用mock功能
      })
    ],
    // vite 相关配置
    server: {
      host: '0.0.0.0',
      port: port,
      open: true,
      proxy: {
        [VITE_APP_BASE_API]: {
          target: VITE_SERVE,
          changeOrigin: true,
          rewrite: path => path.replace(RegExp(`^${VITE_APP_BASE_API}`), '')
        }
      },
      disableHostCheck: true
    },
    css: {
      postcss: {
        plugins: [
          {
            postcssPlugin: 'internal:charset-removal',
            AtRule: {
              charset: (atRule) => {
                if (atRule.name === 'charset') {
                  atRule.remove();
                }
              }
            }
          }
        ]
      }
    },
    build: {
      rollupOptions: {
        output: {
          chunkFileNames: 'static/js/[name]-[hash].js',
          entryFileNames: 'static/js/[name]-[hash].js',
          assetFileNames: 'static/[ext]/[name]-[hash][extname]',
          manualChunks(id) {
            if (id.includes('element-plus')) {
              return 'element-plus';
            }
          }
        }
      }
    }
  }
})

完整版package.json文件

{
  "name": "yf-blog",
  "author": "nch",
  "private": true,
  "version": "0.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc -b && vite build",
    "preview": "vite preview"
  },
  "repository": {
    "type": "git",
    "url": "https://gitee.com/niech_project/***.git"
  },
  "dependencies": {
    "@element-plus/icons-vue": "^2.3.1",
    "axios": "^1.7.9",
    "element-plus": "^2.9.0",
    "file-saver": "^2.0.5",
    "highlight.js": "^11.10.0",
    "js-cookie": "^3.0.5",
    "marked": "^15.0.3",
    "pinia": "^2.3.0",
    "pinia-plugin-persistedstate": "^4.1.3",
    "vue": "^3.5.13",
    "vue-router": "^4.5.0"
  },
  "devDependencies": {
    "@types/file-saver": "^2.0.7",
    "@types/js-cookie": "^3.0.6",
    "@types/mockjs": "^1.0.10",
    "@types/node": "^22.10.2",
    "@vitejs/plugin-vue": "^5.2.1",
    "cross-env": "^7.0.3",
    "mockjs": "^1.1.0",
    "sass": "^1.83.0",
    "sass-loader": "^16.0.4",
    "typescript": "~5.6.2",
    "unplugin-auto-import": "^0.18.6",
    "unplugin-vue-components": "^0.27.5",
    "vite": "^6.0.1",
    "vite-plugin-mock": "^3.0.2",
    "vue-tsc": "^2.1.10"
  }
}

至此,前端项目基本架构完成。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261866.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

生活小妙招之UE CaptureRT改

需求&#xff0c;四个不同的相机拍摄结果同屏分屏显示 一般的想法是四个Capture拍四张RT&#xff0c;然后最后在面片/UI上组合。这样的开销是创建4张RT&#xff0c;材质中采样4次RT。 以更省的角度&#xff0c;想要对以上流程做优化&#xff0c;4个相机拍摄是必须的&#xff…

【AIGC进阶-ChatGPT提示词副业解析】探索生活的小确幸:在平凡中寻找幸福

引言 在这个快节奏的现代社会中,我们常常被各种压力和焦虑所困扰,忘记了生活中那些细小而珍贵的幸福时刻。本文将探讨如何在日常生活中发现和珍惜那些"小确幸",以及如何通过尝试新事物来丰富我们的生活体验。我们还将讨论保持神秘感和期待感对于维持生活乐趣的重要性…

C#编程报错- “ComboBox”是“...ComboBox”和“...ComboBox”之间的不明确的引用

1、问题描述 在学习使用C#中的Winform平台编写一个串口助手程序时&#xff0c; 在编写一个更新ComboBox列表是遇到了问题&#xff0c;出错的代码是 2、报错信息 CS1503 参数 2: 无法从“System.Windows.Forms.ComboBox”转换为“System.Windows.Forms.ComboBox” CS1503 …

ollama+open-webui,本地部署自己的大模型

目录 一、效果预览 二、部署ollama 1.ollama说明 2.安装流程 2.1 windows系统 2.1.1下载安装包 2.1.2验证安装结果 2.1.3设置模型文件保存地址 2.1.4拉取大模型镜像 2.2linux系统 2.2.1下载并安装ollama 2.2.2设置环境变量 2.2.3拉取模型文件 三、部署open-webui…

leetcode_203. 移除链表元素

203. 移除链表元素 - 力扣&#xff08;LeetCode&#xff09; 开始写的时候没有想明白的问题 1. 开始我是想头节点 尾节点 中间节点 分开处理 如果删除的是头节点 然后又要删除头节点的后继节点 那么 这样子的话头节点分开处理就毫无意义了 接着是尾节点 开始我定义的是curr h…

【大模型微调学习5】-大模型微调技术LoRA

【大模型微调学习5】-大模型微调技术LoRA LoRa微调1.现有 PEFT 方法的局限与挑战2.LoRA: 小模型有大智慧 (2021)3.AdaLoRA: 自适应权重矩阵的高效微调 (2023)4.QLoRA: 高效微调量化大模型 (2023) LoRa微调 1.现有 PEFT 方法的局限与挑战 Adapter方法&#xff0c;通过增加模型深…

.NET 技术系列 | 通过CreatePipe函数创建管道

01阅读须知 此文所提供的信息只为网络安全人员对自己所负责的网站、服务器等&#xff08;包括但不限于&#xff09;进行检测或维护参考&#xff0c;未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作。利用此文所提供的信息而造成的直接或间接后果和损失&#xf…

DS18B20温度传感器(STM32)

一、介绍 DS18B20是一种常见的数字型温度传感器&#xff0c;具备独特的单总线接口方式。其控制命令和数据都是以数字信号的方式输入输出&#xff0c;相比较于模拟温度传感器&#xff0c;具有功能强大、硬件简单、易扩展、抗干扰性强等特点。 传感器参数 测温范围为-55℃到1…

shell编程2 永久环境变量和字符串显位

声明 学习视频来自B站UP主 泷羽sec 常见变量 echo $HOME &#xff08;家目录 root用户&#xff09; /root cd /root windows的环境变量可以去设置里去新建 为什么输入ls dir的命令的时候就会输出相应的内容呢 因为这些命令都有相应的变量 which ls 通过这个命令查看ls命令脚本…

MaskGCT——开源文本转语音模型,可模仿任何人说话声音

前期介绍过很多语音合成的模型&#xff0c;比如ChatTTS&#xff0c;微软语音合成大模型&#xff0c;字节跳动自家发布的语音合成模型Seed-TTS。其模型随着技术的不断发展&#xff0c;模型说话的声音也越来越像人类&#xff0c;虽然 seed-tts 可以进行语音合成等功能&#xff0c…

java全栈day16--Web后端实战(数据库)

一、数据库介绍 二、Mysql安装&#xff08;自行在网上找&#xff0c;教程简单&#xff09; 安装好了进行Mysql连接 连接语法&#xff1a;winr输入cmd&#xff0c;在命令行中再输入mysql -uroot -p密码 方法二&#xff1a;winr输入cmd&#xff0c;在命令行中再输入mysql -uroo…

geoserver 瓦片地图,tomcat和nginx实现负载均衡

在地理信息系统&#xff08;GIS&#xff09;领域&#xff0c;GeoServer作为一个强大的开源服务器&#xff0c;能够发布各种地图服务&#xff0c;包括瓦片地图服务。为了提高服务的可用性和扩展性&#xff0c;结合Tomcat和Nginx实现负载均衡成为了一个有效的解决方案。本文将详细…

达梦8-达梦数据的示例用户和表

1、示例库说明&#xff1a; 创建达梦数据的示例用户和表&#xff0c;导入测试数据。 在完成达梦数据库的安装之后&#xff0c;在/opt/dmdbms/samples/instance_script目录下有用于创建示例用户的SQL文件。samples目录前的路径根据实际安装情况进行修改&#xff0c;本文将达梦…

利用notepad++删除特定关键字所在的行

1、按组合键Ctrl H&#xff0c;查找模式选择 ‘正则表达式’&#xff0c;不选 ‘.匹配新行’ 2、查找目标输入 &#xff1a; ^.*关键字.*\r\n (不保留空行) ^.*关键字.*$ (保留空行)3、替换为&#xff1a;&#xff08;空&#xff09; 配置界面参考下图&#xff1a; ​​…

Qt学习笔记第61到70讲

第61讲 记事本实现当前行高亮功能 实现策略&#xff1a; 获取当前行的光标位置&#xff0c;使用的信号和获取行列值是一样的&#xff0c;即通过ExtraSelection 来配置相关属性。 关键API&#xff1a; QList<QTextEdit::ExtraSelection> extraSelections; void setExtraSe…

Axure高保真数据可视化大屏图表组件库

推出了一款高保真数据可视化大屏图表组件库&#xff0c;旨在为用户提供丰富的图表类型&#xff0c;使数据呈现更加直观、生动。本文将详细介绍该组件库中的各类图表元件&#xff0c;包括面积图、折线图、柱状图、条形图、圆环图、雷达图、仪表图以及综合类图表&#xff0c;以满…

【机器学习】在向量的流光中,揽数理星河为衣,以线性代数为钥,轻启机器学习黎明的瑰丽诗章

文章目录 线性代数入门&#xff1a;机器学习零基础小白指南前言一、向量&#xff1a;数据的基本单元1.1 什么是向量&#xff1f;1.1.1 举个例子&#xff1a; 1.2 向量的表示与维度1.2.1 向量的维度1.2.2 向量的表示方法 1.3 向量的基本运算1.3.1 向量加法1.3.2 向量的数乘1.3.3…

【鸿睿创智开发板试用】RK3568 NPU的人工智能推理测试

目录 引言 驱动移植 例程编译 修改build.sh 执行编译 运行测试 部署libc的库文件 执行测试程序 结语 引言 鸿睿创智的H01开发板是基于RK3568芯片的&#xff0c;瑞芯微芯片的一大特色就是提供了NPU推理的支持。本文将对其NPU推理进行测试。 驱动移植 H01的开发板已经…

3D相框案例讲解(详细)

前言 通过现阶段的学习&#xff0c;我们已经掌握了HTML&#xff0c;CSS和JS部分的相关知识点&#xff0c;现在让我们通过一篇案例&#xff0c;来巩固我们近期所学的知识点。 详细视频讲解戳这里 任务一 了解目标案例样式 1.1了解案例 3D相框 1.2 分析案例 首先我们看到一个…

【试听开放中】新中地2408期GIS特训营同步,学习路线图+职业规划

很多地信相关专业的同学都知道 学习开发很重要&#xff1f;项目实战经验很重要 但是在面对学什么语言&#xff1f;掌握什么技术的时候 同学又就开始犯嘀咕&#xff0c;这么多&#xff0c;到底学什么&#xff1f; 基础入门 1.掌握GIS基础的理论知识 2.HTML5标签搭建页面结构…