工作中遇到的问题与解决办法(三)

Vite+vue3+ts

创建Vue3项目

检查工程文件夹

cmd进入文件夹

npm init vite(初始化工程)

安装依赖

npm install

启动项目

npm run dev

setup语法糖

js 复制代码
<script setup lang='ts' name='hello'>
</script>

ref()响应式数据

js要用.value,模板里面不用

ref用于定义基本类型数据,js里面需要.value

reactive用于定义对象类型数据,js里面不需要.value

toRefs()转换

vue3生命周期

创建(steup)

挂载

onMount()

更新

onUpdated()

卸载(销毁)

onUnMounted()

路由

src取别名

安装path模块

npm install --save-dev @types/node

修改 vite.config.ts 文件

tsx 复制代码
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from 'path'

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

修改tsconfig.js

compilerOptions对象添加

js 复制代码
"baseUrl": "./", // 解析非相对模块的基地址,默认是当前目录
    "paths": { //路径映射,相对于baseUrl
      "@/*": ["src/*"]
    }

安装最新版路由

javascript 复制代码
npm install vue-router@next --save 
或者npm install vue-router@4(vue3 版本)
npm install vue-router@3(vue2 版本)

新建router.index.ts

tsx 复制代码
import { createRouter, createWebHistory} from "vue-router"

//创建路由
const router = createRouter({
    history: createWebHistory(),
    routes:[
        //首页
        {
            path: '/home',
            name: 'home',
            component: () => import('@/views/Home.vue'),
        },
        //测试页面
        {
            path: '/test',
            name: 'test',
            component: () => import('@/views/test/index.vue'),
        },
    ]
})
//导出路由
export default router;

main.ts中使用路由

js 复制代码
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
//路由
import router from "./router/index.ts"
const app = createApp(App);
//使用路由
app.use(router)
app.mount('#app')

App.vue中修改内容,使用占位

vue 复制代码
<script setup lang="ts">
</script>

<template>
<RouterView></RouterView>
</template>

<style scoped>

</style>

这样浏览器url展示对应的vue页面

history与hash模式

编程式路由导航

router.push()

存储区

这里使用pinia

安装

npm i pinia

安装依赖

main.ts全局使用

js 复制代码
import {createPinia} from 'pinia'
const pinia = createPinia()
use(pinia);

store文件夹下面创建userStore.js

js 复制代码
import { defineStore } from "pinia";

export const userStore = defineStore("userStore", {
  state: () => {
    return {
      token:'',
      userId: null,
      userName: null
    };
  },
  getters: {},
  actions: {},
});

使用

js 复制代码
<script setup>
import { userStore } from '@/store/userStore'
const user = userStore()
const userId = user.userId
</script>

环境配置

生产、开发、测试环境

.env.development

# 页面标题
VITE_APP_TITLE = 首页
# 开发环境配置
VITE_APP_ENV = 'development'
# 微信公众号/开发环境
VITE_APP_BASE_API = '/dev-api'

.env.production

# 页面标题
VITE_APP_TITLE = 首页

# 生产环境配置
VITE_APP_ENV = 'production'

# 微信公众号/生产环境
VITE_APP_BASE_API = '/prod-api'

# 是否在打包时开启压缩,支持 gzip 和 brotli
VITE_BUILD_COMPRESS = gzip

.env.test

# 页面标题
VITE_APP_TITLE = 首页
# 测试环境配置
VITE_APP_ENV = 'test'
# 微信公众号/开发环境
VITE_APP_BASE_API = '/test-api'

项目启动端口与后端请求地址

vite.config.ts 新增

tsx 复制代码
// vite 相关配置
  server: {
    port: 8080,
    host: true,
    open: true,
    proxy: {
      // https://cn.vitejs.dev/config/#server-proxy
      '/dev-api': {
        target: 'http://localhost:8081',
        changeOrigin: true,
        rewrite: (p) => p.replace(/^\/dev-api/, '')
      }
    }
  },

路径变化,页面title发生变化

router文件

js 复制代码
import { createRouter, createWebHistory} from "vue-router"

//创建路由
const router = createRouter({
    history: createWebHistory(),
    routes:[
        //首页
        {
            path: '/index',
            name: 'index',
            component: () => import('@/views/index.vue'),
            meta:{
                hidden:true,
                title:"首页"
            }
        },
        //测试页面
        {
            path: '/test',
            name: 'test',
            component: () => import('@/views/test/index.vue'),
            meta:{
                hidden:true,
                title:"测试页面"
            }
        },
    ]
})
// 全局前置守卫
router.beforeEach((to, from, next) => {
    // 在这里设置页面的标题
    document.title = to.meta.title || '首页';
    next();
});

//导出路由
export default router;

svg图标库配置

安装

npm i vite-plugin-svg-icons -D
or
pnpm install vite-plugin-svg-icons -D

安装

pnpm install fast-glob -D

vite.config.ts配置插件

tsx 复制代码
import { createSvgIconsPlugin } from "vite-plugin-svg-icons";
plugins: [
      vue(),
      createSvgIconsPlugin({
        // 指定需要缓存的图标文件夹
        iconDirs: [path.resolve(process.cwd(), "src/assets/icons/svg")],
        // 指定symbolId格式
        symbolId: "icon-[dir]-[name]",
      }),

main.ts导入

tsx 复制代码
//svg
import 'virtual:svg-icons-register'   

阿里云矢量图标库复制svg代码到存放文件夹

页面使用

vue 复制代码
  <svg style="width: 30px;height: 30px">
    <use xlink:href="#icon-yygh"></use>  //use 使用yygh.svg图标
  </svg>

封装请求方法

安装axios

cnpm install axios

安装vant

cnpm instanll vant

main.ts全局使用

tsx 复制代码
// 全局引入 Vant 所有组件
import Vant from 'vant';
import 'vant/lib/index.css';
app.use(Vant)

新建utils/request.ts

tsx 复制代码
import axios, { AxiosInstance, AxiosResponse }from "axios";
import { showNotify } from 'vant';
//引入store
import {userStore} from '@/store/userStore.ts'
const user = userStore();
// 创建axios实例
export const service:AxiosInstance = axios.create({
  //请求地址
  baseURL: import.meta.env.VITE_APP_BASE_API,
  // 超时
  timeout: 10000 * 20,
  //请求头
  headers: {
    'Content-Type': 'application/json;charset=utf-8',
  },
});
// request拦截器,请求体加上token
service.interceptors.request.use(
    (config: any) => {
        // prettier-ignore
        config.headers["Authorization"] = "Bearer " + user.token; // 让每个请求携带自定义token 请根据实际情况自行修改
      return config;
    },
    (error) => {
      Promise.reject(error);
    }
);


//响应拦截
service.interceptors.response.use((res:AxiosResponse) => {
  // 未设置状态码则默认成功状态
  const code = res.data.code;
  // 获取错误信息
  // prettier-ignore
  const msg = res.data.msg;
  // 二进制数据则直接返回
  // prettier-ignore
  if (res.request.responseType === "blob" || res.request.responseType === "arraybuffer") {
    return res.data;
  };
  //返回状态码判断
  if (code === 401) {
    // 说明不通过微信公众号调用
    showNotify({ type: 'danger', message: '请关注微信公众号使用!' });
    // prettier-ignore
    return Promise.reject("请关注微信公众号!");
  } else if (code === 500) {
    showNotify({ type: 'danger', message: msg });
    return Promise.reject(new Error(msg));
  } else if (code !== 200) {
    showNotify({ type: 'danger', message: msg });
    return Promise.reject("error");
  } else {
    return res.data;
  }
},  (error) => {
  let { message } = error;
  if (message == "Network Error") {
    message = "后端接口连接异常";
    showNotify({ type: 'danger', message: message });
  } else if (message.includes("timeout")) {
    message = "系统接口请求超时";
    showNotify({ type: 'danger', message: message });
  } else if (message.includes("Request failed with status code")) {
    message = "系统接口" + message.substr(message.length - 3) + "异常";
    showNotify({ type: 'danger', message: message });
  }
  return Promise.reject(error);

});


//对外暴露
export default service;

定义请求方法

tsx 复制代码
import request from "@/utils/request";


/**
 * 测试查询
 */
export const getTset = async () => {
    return await request({
        url: "/api/test",
        method: "get",
    });
};

测试调用

tsx 复制代码
<script setup lang="ts">
import {getTset} from "@/api/test";

function t(){
  getTset().then(response => {
    console.log(response);
  });
}
t();
</script>

springboot 获得token

java 复制代码
@Controller
public class TestController {
    @GetMapping("/api/test")
    @ResponseBody
    public AjaxResult test(@RequestHeader("Authorization") String authHeader){
        // 假设token是Authorization头中的值,并且以"Bearer "开头
        String token = authHeader.replaceFirst("Bearer ", "");
        System.out.println(token+"??");
        return AjaxResult.success("xhengg");
    }
}

element ui plus 显示中文

import zhCn from 'element-plus/es/locale/lang/zh-cn'
app.use(ElementPlus,{locale:zhCn})

element ui plus 单选框修改选中与不选择样式

css 复制代码
/* 覆盖被选中的el-radio样式 */
:deep.el-radio.is-checked .el-radio__inner {
  border-color: #05BF9A !important; /* 选中时的边框颜色 */
  background-color: #05BF9A !important; /* 选中时的背景颜色 */
}

/* 当el-radio被选中时,改变对勾的样式 */
:deep.el-radio.is-checked .el-radio__label {
  color: #05BF9A !important;
}

/* 覆盖被选中的el-radio样式 */
:deep.el-radio.is-disabled .el-radio__inner {
  border-color:  #b1b3b8 !important; /* 选中时的边框颜色 */
  background-color:  #b1b3b8 !important; /* 选中时的背景颜色 */
}

c-loading修改样式

css

:deep .el-loading-spinner .path{
  stroke: #32b16c;
}

倒计时

初始化

tsx 复制代码
const showDialog =()=>{
  dialogVisible.value = true;
  countDown.value = 30;
  const x = setInterval(updateCountdown, 1000);
}

开始倒计时

tsx 复制代码
const updateCountdown=() =>{
  if (countDown.value > 0) {
    countDown.value--;
  } else {
    
  }
}

CLODOP打印

打印PDF base64文件

1,官网下载CLODOP

2,将安装包下LodopFuncs.js 加入到工程项目

2,打印方法

tsx 复制代码
const pdfbase64 = "要打印的pdf base64编码"
let LODOP=getLodop();
  if(LODOP == null){
    ElMessageBox.confirm('CLODDP插件加载失败!', '提示', {
      confirmButtonText: '确定',
      type: 'warning',
      confirmButtonClass: 'bt'

    })
    visiable.value = false;
    return;
  }
  //判断打印机
  const counter = LODOP.GET_PRINTER_COUNT() // 获取打印机个数
  if(counter == 6){
    ElMessageBox.confirm('此电脑没有连接打印机!', '提示', {
      confirmButtonText: '确定',
      type: 'warning',
      confirmButtonClass: 'bt'
    })
    visiable.value = false;
    return;
  }
  //选初始化
  LODOP.PRINT_INIT("门诊报告打印");
  //设置打印机
  const name = LODOP.GET_PRINTER_NAME(0);
  console.log(name)
  if(!LODOP.SET_PRINTER_INDEX(0)){
    ElMessageBox.confirm('打印机不存在', '提示', {
      confirmButtonText: '确定',
      type: 'warning',
      confirmButtonClass: 'bt'
    })
    return;
  }
  //开始打印
  LODOP.ADD_PRINT_PDF(0,0,"100%","100%",pdfbase64.value);
  //LODOP.PREVIEW();
  LODOP.PRINT();
  //打印结果判断
  LODOP.On_Return=function(TaskID,Value){
    if(Value == true){
      showDialog();
    }else{
      ElMessageBox.confirm('打印失败,请联系管理员!', '提示', {
        confirmButtonText: '确定',
        type: 'warning',
        confirmButtonClass: 'bt'
      })
    }
  };

自定义简单

js 复制代码
  LODOP.ADD_PRINT_TEXT(70,0,"100%","100%","测试文本");//top,left,width,heigh,string
  LODOP.SET_PRINT_STYLEA(0,"Fontsize",30);//字体大小
  LODOP.SET_PRINT_STYLEA(0,"Alignment",2);//文本框里 内容对于文本框居中,
  LODOP.SET_PRINT_STYLEA(0,"Horient",2);//打印项在纸张中水平居中
  LODOP.ADD_PRINT_HTM(840,0,1200,"100%","<hr>")//html代码横线

el-date-picker定义时间范围为最近一个月

tsx 复制代码
const end = new Date()
const start = new Date()
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)
//查询条件
const mzfyQuery=ref({
  PatientId:wxUserStore().patientId,
  StartDate:null,
  EndDate:null,
  Date:[start,end],
})

倒计时

tsx 复制代码
//倒计时
const seconds = ref(120);
const startCountdown = () => {
  const intervalId = setInterval(() => {
    if (seconds.value > 0) {
      seconds.value -= 1;
    } else {
      // 点击确认执行的逻辑
      router.push("/index");;
    }
  }, 1000);
};
//开始倒计时
startCountdown();

页面自适应

vw vh

.centerCard{
  width: 80vw;
  height: 60vh;
}

el-button 图标大小

js 复制代码
<el-icon class="el-icon--right" style="font-size: 3vw" ><CloseBold/></el-icon>

修改字体

深度选择器

el-select

css 复制代码
::v-deep .el-select .el-select__placeholder {
  font-size: 1.25vw;

el-radio

css 复制代码
::v-deep .el-radio-group .el-radio__label {
  font-size: 1.25vw; /* 设置你想要的字体大小 */

}

el-form-item label字体

css 复制代码
<el-form-item  prop="sex">
  <template #label>
    <span style="font-size: 1.5vw">性&nbsp;&nbsp;别</span>
  </template>
</el-form-item>

el-date-picker一年以内日期

vue3+ts

vue 复制代码
<el-date-picker
  v-model="dateRangeValue"
  type="daterange"
  format="YYYY-MM-DD"
  value-format="YYYY-MM-DD"
  range-separator="至"
  start-placeholder="开始日期"
  end-placeholder="结束日期"
  style="width: 20vw;height: 5vh;font-size: 2vw"
/>
ts 复制代码
// 初始化日期范围
const dateRangeValue = ref(["2020-03-01","2024-07-03"]);
const initQuery = ()=>{
// 创建一个新的Date对象,这将自动设置为当前日期和时间
  let currentDate = new Date();
// 获取年份、月份(注意月份是从0开始的,所以需要+1)和日期
  let year = currentDate.getFullYear();
  let yearLast = currentDate.getFullYear()-1;
  let month = String(currentDate.getMonth() + 1).padStart(2, '0'); // padStart用于确保月份为两位数
  let day = String(currentDate.getDate()).padStart(2, '0'); // padStart用于确保日期为两位数
// 将它们组合成"YYYY-MM-DD"的格式
  let endDate = `${year}-${month}-${day}`;
  let startDate = `${yearLast}-${month}-${day}`;
  let temp = [startDate,endDate];
  dateRangeValue.value = temp;

}
initQuery()

eldialog

文字居中

vue 复制代码
<span style="font-size: 2vw;display: block;text-align: center;">请将身份证放到如图所示区域</span>

img居中

vue 复制代码
<div class="img-center">
  <img style="width:30vw;height: 10vh;display: block;text-align: center;" src="/src/img/sfzys.png" >
</div>
css 复制代码
.img-center {  

  display: flex;  

  justify-content: center; /* 水平居中 */  

  align-items: center; /* 垂直居中 */  

  height: 100%; /* 确保 div 的高度与其父元素相同 */  

}

div 位于页面中央

vue 复制代码
<script setup lang="ts">
import {ref} from 'vue'
import router from "@/router";
//父组件传来的信息
import { defineProps } from 'vue';
const props = defineProps({
  fatherMsg: {
    type: String,
    required: true,
  },
});

//倒计时
const seconds = ref(180);
const startCountdown = () => {
  const intervalId = setInterval(() => {
    if (seconds.value > 0) {
      seconds.value -= 1;
    } else {
      // 点击确认执行的逻辑
      router.push("/index");;
    }
  }, 1000);
};
//开始倒计时
startCountdown();
//重置倒计时
const resetCountdown = ()=>{
  seconds.value = 180;
}
const alterDialogVisible = ref(false);
//返回

const exit = ()=>{
  alterDialogVisible.value = true;
}
const cancelExit = () => {
  alterDialogVisible.value = false;
}
const confirmExit = () => {
  alterDialogVisible.value = false
  router.push("/index")
}
</script>

<template>
<div style="width: 90vw;height: 4vw;margin: 0 auto; /* 上下外边距为0,左右外边距自动 */  " >
  <div style="width: 45vw;height: 100%;float: left">
    <el-row>
      <el-button style="width: 15vw;height: 3vw" round @click="exit"><span style="color: red;font-size: 2vw">{{seconds}}s</span ><span style="font-size: 2vw;color: #05BF9A">自动退出</span></el-button>
      <el-button style="height:100%;" round @click="resetCountdown"><span style="font-size: 1.5vw;color: #E6A23C">重置</span></el-button>
    </el-row>
  </div>
  <div style="width: 45vw;height: 100%;float: right">
    <el-button round style="background-color: #05BF9A;color: white;width: 7vw;height: 3vw;float: right" type="primary" @click="exit"><span style="font-size: 2vw">退出</span></el-button>
  </div>
</div>
  <el-dialog v-model="alterDialogVisible"  width="50vw" center height="15vh" style="margin-top: 50vh" >
    <template #title>
      <el-icon style="font-size: 3vw;color: #E6A23C"><WarningFilled /></el-icon>
      <span style="font-size: 3vw">温馨提示</span>
    </template>
    <span style="font-size: 2vw;display: block;text-align: center;">
      {{props.fatherMsg}}
    </span>
    <template #footer>
      <div class="dialog-footer">
        <el-button style="width: 12vw;height: 3vw" @click="cancelExit"><span style="font-size: 2vw;color: #b1b3b8">取消</span></el-button>
        <el-button style="background-color: #05BF9A;color: white;width: 10vw;height: 3vw" type="primary" @click="confirmExit"><span style="font-size: 2vw">确定</span></el-button>
      </div>
    </template>
  </el-dialog>
</template>

<style>
.el-dialog {
  border-radius: 30px;
}
</style>

子组件往父组件发消息

子组件

js 复制代码
//定义
import { defineEmits } from 'vue';
const emit = defineEmits(['childMsg']);
//通过事件提交
emit('childMsg', "1");

父组件获取

js 复制代码
//定义获取子组件数据
const childMsg = (value: string) => {
  console.log("子组件返回数据"+value)
};
//父组件使用子组件
<childVue @child-msg="childMsg"/>
相关推荐
孙 悟 空2 天前
ArcGIS Maps SDK for JavaScript:根据经纬度定位,并添加定位标记
javascript·arcgis
GIS思维2 天前
ArcGIS Pro品字型标记符号填充
arcgis·arcgis pro·arcgis pro符号填充
wuningw2 天前
ant-design-ui的Select选择器多选时同时获取label与vaule值
ui·arcgis
GISerQ.3 天前
ArcGIS计算土地转移矩阵
arcgis·土地利用·土地转移矩阵
小仙有礼了4 天前
Arcgis for javascript 开发学习经验
javascript·学习·arcgis
规划GIS会4 天前
【ArcGIS Pro】实现一下完美的坐标点标注
arcgis
中科GIS地理信息培训4 天前
ArcGIS Pro 3.4新功能3:空间统计新特性,基于森林和增强分类与回归,过滤空间自相关
arcgis·分类·回归·arcgis pro
赵钰老师5 天前
【ArcGIS Pro】水文水资源、水生态与水环境
人工智能·python·机器学习·arcgis·chatgpt·数据分析
中科GIS地理信息培训5 天前
ArcGIS Pro 3.4新功能2:Spatial Analyst新特性,密度、距离、水文、太阳能、表面、区域分析
arcgis·arcgis pro
规划GIS会5 天前
【ArcGIS Pro】做个宽度渐变的河流符号
arcgis