目录
写配置
创建vue项目
npm init vite@latest beimao-h5 -- --template vue-ts
我们使用的是-h5
npm i @nutui/nutui
在src里面的main文件中
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// 注意:这种方式将会导入所有组件
import NutUI from "@nutui/nutui";
// 采用按需加载时 此全局css样式,需要删除
import "@nutui/nutui/dist/style.css";
createApp(App).use(NutUI).mount("#app");
插件在package.json
{
"name": "beimao-h5",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
},
"dependencies": {
"@nutui/nutui": "^3.1.22",
"axios": "^0.27.2",
"dayjs": "^1.11.3",
"path": "^0.12.7",
"pinia": "^2.0.14",
"pinia-plugin-persist": "^1.0.0",
"ts-md5": "^1.2.11",
"vue": "^3.2.37",
"vue-router": "^4.0.16"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.0.0",
"typescript": "^4.6.4",
"vite": "^3.0.0",
"vue-tsc": "^0.38.4"
}
}
加入后进行安装
npm install
在src的main.ts里面进行加入东西
import { createApp } from 'vue'
import App from './App.vue'
//导入我们的路由文件
import Router from './router/index'
//导入store
import store from './store/index'
// 注意:这种方式将会导入所有组件
import NutUI from "@nutui/nutui";
// 采用按需加载时 此全局css样式,需要删除
import "@nutui/nutui/dist/style.css";
const app = createApp(App)
//使用京东 NutUI
.use(NutUI)
//实用路由
app.use(Router)
//使用store
app.use(store);
app.mount('#app')
根据里面的内容,加入相应的文件夹以及ts文件
加入router文件夹,在里面创建index.ts文件,这里面是路由
import { createRouter, createWebHashHistory } from "vue-router";
const routes = [
{
path: '/',
name: "home",
component: () => import("@/views/Index.vue"),
},
{
path: '/category',
name: "category",
component: () => import("@/views/Category.vue"),
},
]
const router = createRouter({
history: createWebHashHistory('/'),
routes
})
export default router
建立store文件夹,在里面创建index.ts文件以及appStore.ts文件
index.ts文件
import { createPinia } from 'pinia'
import piniaPluginPersist from 'pinia-plugin-persist'
const store = createPinia()
store.use(piniaPluginPersist)
export default store
appStore.ts文件
import { defineStore } from 'pinia'
const appStore = defineStore({
id: 'app',
state: () => {
return {
user: { id: 0, userId: "" },
token: "",
}
},
getters: {
},
actions: {
},
// 开启数据缓存
persist: {
enabled: true,
strategies: [
{
key: 'com.beiyou.h5',
storage: localStorage,
}
]
}
})
export default appStore;
建立views文件夹,里面建立Index.vue
Index.vue
<template>
<div>你好苍老师</div>
</template>
在在外层的vite.config.ts文件中进行配置文件
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import path from "path";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()],
server: {
port: 4000, // 你需要定义的端口号
proxy: {
"/api": {
target: "http://localhost:8080/",
changeOrigin: true,
},
},
},
resolve: {
// 配置路径别名
alias: {
'@': path.resolve(__dirname, './src'),
}
},
})
当path爆红的时候,安装path
npm install path
删除components里面的文件
在src文件夹下面的App文件里面修改内容
<template>
<div>
home
</div>
</template>
<script setup lang="ts">
</script>
建立http文件夹管理页面拦截
建立index.ts
import axios, {
AxiosRequestConfig,
AxiosRequestHeaders,
AxiosResponse,
} from "axios";
import { storeToRefs } from "pinia";
import appStore from "@/store/appStore";
let { token } = storeToRefs(appStore());
const state = {
ok: 0,//请求成功状态码
401: "ERR_BAD_REQUEST"
};
//返回数据规则
interface IResponseData<T> {
status: number;
message?: string;
data: T;
code: string;
}
//请求默认配置规则
type TOption = {
baseURL: string;
timeout: number;
};
//默认配置
const config = {
baseURL: "",
timeout: 30 * 1000,
withCredentials: true,
};
let loading: any = null;
class Http {
service: any;
constructor(config: TOption) {
//实例化请求配置
this.service = axios.create(config);
//请求拦截
this.service.interceptors.request.use(
(config: AxiosRequestConfig) => {
// loading = ElLoading.service({ fullscreen: true, text: '加载中...' });
if (token.value) {
(config.headers as AxiosRequestHeaders).Authorization = token.value;
}
return config;
},
(error: any) => {
loading.close();
return Promise.reject(error);
}
);
//响应拦截
this.service.interceptors.response.use(
(response: AxiosResponse) => {
// loading.close();
const data = response.data;
const { code } = data;
if (code == undefined) {
//如果没有返回状态码,直接返回数据,针对于返回数据为blob类型
return response;
} else if (code !== 0) {
//ElMessage.error(data.message);
return Promise.reject(data);
}
// code == 0 的时候,提取我们只关注的Api数据data
// console.log(response);
return response.data.data;
},
(error: any) => {
loading.close();
if (error.code === state[401]) {
// ElMessage.error("请求失败:" + error.message);
setTimeout(() => {
localStorage.removeItem('com.beiyou.h5')
window.location.href = '/#/login'
}, 1000);
}
return Promise.reject(error);
}
);
}
get<T>(url: string, params?: object, data = {}): Promise<IResponseData<T>> {
return this.service.get(url, { params, ...data });
}
post<T>(url: string, params?: object, data = {}): Promise<IResponseData<T>> {
return this.service.post(url, params, data);
}
put<T>(url: string, params?: object, data = {}): Promise<IResponseData<T>> {
return this.service.put(url, params, data);
}
delete<T>(
url: string,
params?: object,
data = {}
): Promise<IResponseData<T>> {
return this.service.delete(url, { params, ...data });
}
}
export default new Http(config);
有些配置爆红在tsconfig.json里面加入
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}
加入后的整体样子
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"module": "ESNext",
"moduleResolution": "Node",
"strict": true,
"jsx": "preserve",
"sourceMap": true,
"resolveJsonModule": true,
"isolatedModules": true,
"esModuleInterop": true,
"lib": [
"ESNext",
"DOM"
],
"skipLibCheck": true,
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
}
},
"include": [
"src/**/*.ts",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.vue"
],
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
上面就是配置好的全部文件以及文件里面的构成
这里为止我们该有的配置已经配置完毕,接下来就是写界面
写界面
在src的App.vue里面写入
<template>
<router-view></router-view>
<nut-tabbar @tab-switch="tabSwitch" :bottom="true">
<nut-tabbar-item tab-title="首页" icon="home"></nut-tabbar-item>
<nut-tabbar-item tab-title="分类" icon="category"></nut-tabbar-item>
<nut-tabbar-item tab-title="发现" icon="find"></nut-tabbar-item>
<nut-tabbar-item tab-title="购物车" icon="cart"></nut-tabbar-item>
<nut-tabbar-item tab-title="我的" icon="my"></nut-tabbar-item>
</nut-tabbar>
</template>
<script setup lang="ts">
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const tabSwitch = (item: any, index: any) => {
console.log(item, index);
if (index === 1) {
router.push({ name: "category" });
}
if (index === 0) {
router.push({ name: "home" });
}
};
</script>
<template>
<router-view></router-view>
<nut-tabbar @tab-switch="tabSwitch" :bottom="true">
<nut-tabbar-item tab-title="首页" icon="home"></nut-tabbar-item>
<nut-tabbar-item tab-title="分类" icon="category"></nut-tabbar-item>
<nut-tabbar-item tab-title="发现" icon="find"></nut-tabbar-item>
<nut-tabbar-item tab-title="购物车" icon="cart"></nut-tabbar-item>
<nut-tabbar-item tab-title="我的" icon="my"></nut-tabbar-item>
</nut-tabbar>
</template>
<script setup lang="ts">
import { useRoute, useRouter } from "vue-router";
const route = useRoute();
const router = useRouter();
const tabSwitch = (item: any, index: any) => {
console.log(item, index);
if (index === 1) {
router.push({ name: "category" });
}
if (index === 0) {
router.push({ name: "home" });
}
};
</script>
解析:
<router-view></router-view>:支持页面跳转
:bottom="true":让下面的首页等在底部
这里就要在路由里面加入一个分支,是category的路由分支
{
path: '/category',
name: "category",
component: () => import("@/views/Category.vue"),
},
在views里面创建Category.vue文件写入
<template>这是分类页</template>
开始正式写vue
在主页面加入一个滚动的公告栏
<nut-noticebar text="华为畅享9新品即将上市" :scrollable="true" :background="`rgba(251, 248, 220, 1)`" :color="`#D9500B`"></nut-noticebar>
在公告栏下面加入一个轮播图
<nut-swiper :init-page="page" :pagination-visible="true" pagination-color="#426543" auto-play="3000">
<nut-swiper-item v-for="item in list" :key="item">
<img :src="item" alt="" />
</nut-swiper-item>
</nut-swiper>
轮播图的script
import { reactive, toRefs, onMounted } from 'vue';
export default {
setup() {
const state = reactive({
page: 2,
list: [
'https://storage.360buyimg.com/jdc-article/NutUItaro34.jpg',
'https://storage.360buyimg.com/jdc-article/NutUItaro2.jpg',
'https://storage.360buyimg.com/jdc-article/welcomenutui.jpg',
'https://storage.360buyimg.com/jdc-article/fristfabu.jpg'
]
});
onMounted(() => {
setTimeout(() => {
state.list.splice(1, 1);
}, 3000);
});
return { ...toRefs(state) };
}
};
轮播图的样式
<style lang="scss" scoped>
.nut-swiper-item {
line-height: 150px;
img {
width: 100%;
height: 100%;
}
}
</style>