提交 cd7ce6ae 作者: zjt

项目初始化

父级
VITE_BASE_URL = ''
\ No newline at end of file
node_modules
*.local
*.sh
\ No newline at end of file
{
"recommendations": ["johnsoncodehk.volar"]
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
{
"name": "vue-vant-ts",
"private": true,
"version": "0.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"server": "live-server ./ --port=8081",
"deploy": "bash deploy.sh"
},
"dependencies": {
"axios": "^0.26.1",
"pinia": "^2.0.20",
"vant": "^3.4.6",
"vue": "^3.2.25",
"vue-router": "^4.0.14",
"vuex": "^4.0.2"
},
"devDependencies": {
"@types/node": "^17.0.21",
"@types/webpack-env": "^1.16.3",
"@vitejs/plugin-vue": "^2.2.0",
"lib-flexible": "^0.3.2",
"postcss-pxtorem": "^5.1.1",
"sass": "^1.49.9",
"typescript": "^4.5.4",
"vite": "^2.8.0",
"vite-plugin-style-import": "1.4.1",
"vue-tsc": "^0.29.8"
}
}
\ No newline at end of file
<template>
<router-view/>
</template>
<style lang="scss">
#app {
width: 100%;
height: 100%;
}
</style>
import { apiUrl } from '@/const/api';
import axiosInstance, { AxiosResponseProps } from '@/uitls/request';
export const getTest = (params?: any) => {
return axiosInstance.get(apiUrl.TEST_PROXY, { params: params || {} });
};
import axiosInstance, { AxiosResponseProps } from '@/uitls/request'
export const getList = (params: any) => {
return axiosInstance.get("/common/code/logisticsInfo/getLogisticsByOrderId", { params: params || {} });
}
@import './reset.scss';
@import './mixin.scss';
@import './variables.scss';
html {
@include root-font-size();
}
//@author TalkTao
.flex-shrink-0 {
flex-shrink: 0;
}
.flex-grow-0 {
flex-grow: 0;
}
.flex-grow-1 {
flex-grow: 1;
}
.flex-column {
flex-direction: column;
}
.flex-wrap {
flex-wrap: wrap;
}
.object-fit-cover {
object-fit: cover;
}
.border-box {
box-sizing: border-box;
}
.fw-bold {
font-weight: bold;
}
.w-0 {
width: 0;
}
.w-100 {
width: 100%;
}
.w-50{
width: 50%;
}
.h-100 {
height: 100%;
}
.text-ellipsis {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.text-again-center {
text-align: center;
}
.flex-column{
flex-direction: column;
}
@each $key, $val in $fontSizeS {
.fs-#{$key} {
font-size: #{$val};
}
}
//.d-flex {display: flex;}
@each $item in $displayList {
.d-#{$item} {
display: #{$item};
}
}
//.flex-center {justify-content: center;}
@each $commonKey, $commonVal in $flexCommonValues {
.flex-#{$commonKey} {
display: flex;
justify-content: #{$commonVal};
align-items: #{$commonVal};
}
.align-self-#{$commonKey} {
align-self: #{$commonVal};
}
}
//.justify-content-between {justify-content: sapce-between;}
@each $key, $val in $justify-content-values {
.justify-content-#{$key} {
justify-content: #{$val};
}
}
//.align-items-start {align-items-start: flex-start;}
@each $key, $val in $align-items-values {
.align-items-#{$key} {
align-items: #{$val};
}
}
//.postion-absolute {absolute;}
@each $pos in $positionList {
.position-#{$pos} {
position: #{$pos};
}
}
//.overflow-x-scroll {overflow-x:scroll}
@each $dir in $overflowDirs {
.#{$dir}-scroll {
#{$dir}: scroll;
}
}
//pt-5 {padding-top: 1vw}
.rounded-circle {
border-radius: 50%;
}
@for $size from 1 through 50 {
.rounded-#{$size} {
border-radius: #{$size}vw;
}
@each $t_d_key, $t_d_val in $spaceTypes_Dirs {
.#{$t_d_key}-#{$size} {
#{$t_d_val}: #{$size}vw;
}
}
.mx-#{$size} {
margin-left: #{$size}vw;
margin-right: #{$size}vw;
}
.my-#{$size} {
margin-top: #{$size}vw;
margin-bottom: #{$size}vw;
}
.px-#{$size} {
padding-left: #{$size}vw;
padding-right: #{$size}vw;
}
.py-#{$size} {
padding-top: #{$size}vw;
padding-bottom: #{$size}vw;
}
}
//top-0 {top: 0;}
@each $type, $val in $positionTypes {
.#{$type}-0 {
#{$val}: 0;
}
}
//bg-white {background-color: white;}
@each $key, $val in $colors {
.bg-#{"" + $key} {
background-color: #{$val};
}
.text-#{"" + $key} {
color: #{$val};
}
}
//.border-pill {border-radius: 50rem}
@each $key, $val in $radiusList {
.border-#{$key} {
border-radius: #{$val};
}
}
//.opacity-75 {opacity: 0.75}
@for $index from 0 through 4 {
.opacity-#{$index * 25} {
opacity: #{$index * 0.25};
}
}
//.text-center {text-align: center}
@each $var in $textAlignDirs {
.text-#{$var} {
text-align: #{$var};
}
}
//.border-bottom-1 {border-bottom: 1px} .border-bottom {border-bottom: 1px}
@for $size from 0 to 5 {
@each $posKey, $posVal in $positionTypes {
@if $size == 0 {
.border-#{$posKey} {
border-#{$posVal}: 1px solid #e6e6e6;
}
} @else {
.border-#{$posKey}-#{$size} {
border-#{$posVal}: #{$size}px solid #e6e6e6;
}
}
}
}
@mixin placeholderColor($color) {
&::input-placeholder {
/*WebKit browsers*/
color: $color;
}
&::-webkit-input-placeholder {
/*WebKit browsers*/
color: $color;
}
&::-moz-input-placeholder {
/*Mozilla Firefox*/
color: $color;
}
&::-ms-input-placeholder {
/*Internet Explorer*/
color: $color;
}
}
@mixin ellipsisRow($row: 1) {
@debug "$row is #{$row}";
@if $row == 1 {
// @debug "1行";
@extend .text-ellipsis;
} @else {
// @debug "多行";
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box; // 作为弹性伸缩盒子模型显示。
-webkit-box-orient: vertical; // 设置伸缩盒子的子元素排列方式--从上到下垂直排列
-webkit-line-clamp: #{$row}; // 显示的行
}
}
\ No newline at end of file
// 清除浮动
@mixin clearfix {
&:after {
content: "";
display: table;
clear: both;
}
}
// 多行隐藏
@mixin textoverflow($clamp:1) {
display: block;
overflow: hidden;
text-overflow: ellipsis;
-o-text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: $clamp;
/*! autoprefixer: ignore next */
-webkit-box-orient: vertical;
}
//flex box
@mixin flexbox($jc:space-between, $ai:center, $fd:row, $fw:nowrap) {
display: flex;
display: -webkit-flex;
flex: 1;
justify-content: $jc;
-webkit-justify-content: $jc;
align-items: $ai;
-webkit-align-items: $ai;
flex-direction: $fd;
-webkit-flex-direction: $fd;
flex-wrap: $fw;
-webkit-flex-wrap: $fw;
}
/* 移动端页面设计稿宽度 */
$design-width: 750;
/* 移动端页面设计稿dpr基准值 */
$design-dpr: 2;
/* 将移动端页面分为10块 */
$blocks: 10;
/* 缩放所支持的设备最小宽度 */
$min-device-width: 320px;
/* 缩放所支持的设备最大宽度 */
$max-device-width: 750px;
/*
rem与px对应关系,1rem代表html font-size值(为一块的宽度),$rem即为$px对应占多少块
$px $rem
------------- === ------------
$design-width $blocks
*/
/* html根元素的font-size定义,简单地将页面分为$blocks块,方便计算 */
@mixin root-font-size() {
font-size: calc(100vw / $blocks);
body {
@include container-min-width();
}
/* 最小宽度定义 */
@media screen and (max-width: $min-device-width) {
font-size: calc($min-device-width / $blocks);
}
/* 最大宽度定义 */
&[data-content-max] {
body[data-content-max] {
@include container-max-width();
}
@media screen and (min-width: $max-device-width) {
font-size: calc($max-device-width / $blocks);
}
}
}
/* 单位px转化为rem */
@function px2rem($px) {
@return #{$px / $design-width * $blocks}rem;
}
/* 单位rem转化为px,可用于根据rem单位快速计算原px */
@function rem2px($rem) {
@return #{$rem / $blocks * $design-width}px;
}
/**
* 实现固定宽高比
* @param {string} $position: relative 定位方式
* @param {string} $width: 100% 容器宽度
* @param {string} $sub: null 容器的目标子元素
* @param {number} $aspectX: 1 容器宽
* @param {number} $aspectY: 1 容器高
*/
@mixin aspect-ratio($position: relative,
$width: 100%,
$sub: null,
$aspectX: 1,
$aspectY: 1) {
overflow: hidden;
position: $position;
padding-top: percentage($aspectY / $aspectX);
width: $width;
height: 0;
@if $sub==null {
$sub: "*";
}
&>#{$sub} {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
}
}
/* 设置容器拉伸的最小宽度 */
@mixin container-min-width() {
margin-right: auto;
margin-left: auto;
min-width: $min-device-width;
}
/* 设置容器拉伸的最大宽度 */
@mixin container-max-width() {
margin-right: auto;
margin-left: auto;
max-width: $max-device-width;
}
/* 设置字体大小,不使用rem单位, 根据dpr值分段调整 */
@mixin font-size($fontSize) {
font-size: $fontSize / $design-dpr;
[data-dpr="2"] & {
font-size: $fontSize / $design-dpr * 2;
}
[data-dpr="3"] & {
font-size: $fontSize / $design-dpr * 3;
}
}
/* 清除浮动 */
@mixin clearfix {
*zoom: 1;
&:after {
content: "";
display: block;
height: 0;
visibility: hidden;
clear: both;
}
}
/*
*$line:超出显示几行省略号
*$substract:预留区域百分比
*/
@mixin text-overflow($line:1, $substract:0) {
overflow: hidden;
@if $line==1 {
white-space: nowrap;
text-overflow: ellipsis;
width: 100% -$substract;
}
@else {
display: -webkit-box;
-webkit-line-clamp: $line;
-webkit-box-orient: vertical;
}
}
@mixin WH($Width:100%, $Height:100%) {
width: $Width;
height: $Height;
}
\ No newline at end of file
/* http://meyerweb.com/eric/tools/css/reset/
v4.0 | 20180602
License: none (public domain)
*/
html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, menu, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed,
figure, figcaption, footer, header, hgroup,
main, menu, nav, output, ruby, section, summary,
time, mark, audio, video {
margin: 0;
padding: 0;
border: 0;
font-size: 100%;
font: inherit;
vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure,
footer, header, hgroup, main, menu, nav, section {
display: block;
}
/* HTML5 hidden-attribute fix for newer browsers */
*[hidden] {
display: none;
}
body {
line-height: 1;
}
menu, ol, ul {
list-style: none;
}
blockquote, q {
quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
content: '';
content: none;
}
table {
border-collapse: collapse;
border-spacing: 0;
}
img{
border: none;
}
/* 去除iPhone中默认的input样式 */
input[type="submit"],input[type="reset"],input[type="button"],input:focus,button:focus,select:focus,textarea:focus{ outline: none;}
input{-webkit-appearance:none; resize: none; border-radius: 0;}
$background-color: #eeeeee;
$title: 18px;
$primary: #b42e46;
$positionList: (relative, absolute, fixed);
$displayList: (flex, block, inline-block, none);
$flexCommonValues: (
start: flex-start,
center: center,
end: flex-end,
);
$justify-content-values: (
start: flex-start,
center: center,
between: space-between,
around: space-around,
evenly: space-evenly,
end: flex-end,
);
$align-items-values: (
start: flex-start,
center: center,
end: flex-end,
);
$overflowDirs: (overflow, overflow-x, overflow-y);
$spaceTypes_Dirs: (
"p": padding,
"m": margin,
"ps": padding-left,
"pe": padding-right,
"pt": padding-top,
"pb": padding-bottom,
"ms": margin-left,
"me": margin-right,
"mt": margin-top,
"mb": margin-bottom,
);
$positionTypes: (
"top": top,
"bottom": bottom,
"left": left,
"right": right,
"start": left,
"end": right,
);
$colors: (
primary: $primary,
white: #ffffff,
muted: #6c757d,
black: #000,
dark: #333,
bgPrimary: "#303c5a",
red: red,
immediateColor: #94765c,
);
$radiusList: (
circle: 100%,
pill: 50rem,
);
$fontSizeS: (
6: 3.2vw,
5: calc(3.2vw * 1.1),
4: calc(3.2vw * 1.2),
3: calc(3.2vw * 1.3),
2: calc(3.2vw * 1.4),
1: calc(3.2vw * 2.3),
);
$textAlignDirs: (left, center, right);
//导出js变量
:export {
primary: $primary;
bgPrimary: "#303c5a";
muted: #6c757d;
}
<script lang="ts">
export default {
name: "RequestLoading",
};
</script>
<script setup lang="ts">
import { vuexStore } from "@/store";
import { computed, defineComponent, reactive, ref, toRefs, watch } from "vue";
let timer = null;
const useLoading = () => {
const state = reactive({
loadedText: "加载中...",
isLoading: computed(() => vuexStore.state.isLoading),
});
return toRefs(state);
};
const { loadedText, isLoading } = useLoading() as any;
</script>
<template>
<div
v-if="isLoading"
class="requestLoading position-fixed start-0 top-0 w-100 h-100"
>
<div
class="position-absolute start-50 translate-middle loadingBox text-center"
>
<LoadingIcon width="86" height="86" class="loadingIcon" />
<div class="text-white text-center mt-2 fs-4 h-25">{{ loadedText }}</div>
</div>
</div>
</template>
<style scoped lang="scss">
.requestLoading {
z-index: 99999999;
background-color: rgba($color: #000000, $alpha: 0.8);
text-align: center;
.loadingBox {
top: 40%;
left: 40%;
.loadingIcon {
animation: rotateIcon 1s infinite;
}
@keyframes rotateIcon {
from {
transform: rotate(0);
}
to {
transform: rotate(360deg);
}
}
}
}
</style>
\ No newline at end of file
<script lang="ts">
export default {
name: "CustomHeader",
};
</script>
<script setup lang="ts">
import { ref } from "vue";
import { useRoute, useRouter } from "vue-router";
interface topTitleProps {
title?: string;
leftIcon?: string;
leftText?: string;
rightText?: string;
color?: string;
}
// 携带默认值的props
withDefaults(defineProps<topTitleProps>(), {
title: "",
leftIcon: "arrow-left",
leftText: "",
rightText: "",
color: "#333",
});
const emit = defineEmits(["clickLeft", "clickRight"]);
const route = useRoute();
const router = useRouter();
const active = ref(0);
const clickLeft = () => {
if (window.history.length > 1) {
router.back();
} else {
router.push("/");
}
};
const clickRight = () => {
emit("clickRight");
};
</script>
<template>
<van-sticky>
<van-nav-bar
:title="title"
@click-left="clickLeft"
@click-right="clickRight"
left-arrow
>
<template #left>
<van-icon :color="color" size="20" :name="leftIcon" /><span
:style="color"
>{{ leftText }}</span
>
</template>
<template @click-right="onClickRight" #right>
<span :style="color">{{ rightText }}</span>
</template>
</van-nav-bar>
</van-sticky>
</template>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<script lang="ts">
export default{
name: 'TabBar'
}
</script>
<script setup lang="ts">
import { reactive, ref } from 'vue'
interface tabProps {
data?: [
{
title: string
to: string
name: string
icon: string
}
]
}
const props = defineProps<tabProps>()
console.log(props.data,'props.data');
const emit = defineEmits(['chang'])
const active = ref('home')
const onChange = (value) => {
// console.log(value,'value');
emit('chang', value)
}
</script>
<template>
<div>
<van-tabbar fixed route v-model="active" @change="onChange">
<van-tabbar-item v-for="(item, index) in props.data" :to="item.to" :name="item.name" :key="index" :icon="item.icon">{{item.title}}</van-tabbar-item>
</van-tabbar>
</div>
</template>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<script lang="ts">
export default {
name: "LoadingIcon",
};
</script>
<template>
<svg
t="1619142908908"
class="icon"
viewBox="0 0 1024 1024"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
p-id="2450"
width="200"
height="200"
>
<path
d="M899.846 287.713c8.837 15.305 3.593 34.876-11.713 43.713l-152.42 88c-15.306 8.836-34.876 3.592-43.713-11.713-8.837-15.306-3.593-34.876 11.713-43.713l152.42-88c15.306-8.837 34.877-3.593 43.713 11.713zM512 64c17.673 0 32 14.327 32 32v176c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32V96c0-17.673 14.327-32 32-32z m448 448c0 17.673-14.327 32-32 32H752c-17.673 0-32-14.327-32-32 0-17.673 14.327-32 32-32h176c17.673 0 32 14.327 32 32zM736 899.98c-15.305 8.836-34.876 3.592-43.713-11.713-8.836-15.306-3.592-34.877 11.713-43.713 15.305-8.837 34.876-3.593 43.713 11.713 8.836 15.305 3.592 34.876-11.713 43.712zM408 331.866c-15.305 8.836-34.876 3.592-43.713-11.713l-88-152.42c-8.836-15.306-3.592-34.877 11.713-43.713 15.305-8.837 34.876-3.593 43.713 11.712l88 152.42c8.836 15.306 3.592 34.877-11.713 43.714zM305.886 393c-8.837 15.305-28.407 20.55-43.713 11.713l-126.44-73c-15.305-8.837-20.549-28.408-11.712-43.713 8.836-15.305 28.407-20.55 43.712-11.713l126.44 73c15.306 8.837 20.55 28.408 11.713 43.713zM244 512c0 17.673-14.327 32-32 32H96c-17.673 0-32-14.327-32-32 0-17.673 14.327-32 32-32h116c17.673 0 32 14.327 32 32z m9.904 148.713c8.836 15.305 3.592 34.876-11.713 43.713l-74.478 43c-15.306 8.836-34.876 3.592-43.713-11.713-8.837-15.306-3.593-34.876 11.713-43.713l74.478-43c15.305-8.837 34.876-3.593 43.713 11.713zM735.713 124c15.305 8.837 20.55 28.407 11.713 43.713l-88 152.42c-8.837 15.306-28.408 20.55-43.713 11.713-15.306-8.836-20.55-28.407-11.713-43.713l88-152.42c8.837-15.306 28.407-20.55 43.713-11.713z m-387.75 671.349c15.305 8.837 20.55 28.407 11.713 43.713l-28 48.497c-8.837 15.306-28.408 20.55-43.713 11.713-15.306-8.836-20.55-28.407-11.713-43.713l28-48.497c8.837-15.306 28.407-20.55 43.713-11.713zM512 870c17.673 0 32 14.327 32 32v26c0 17.673-14.327 32-32 32-17.673 0-32-14.327-32-32v-26c0-17.673 14.327-32 32-32z"
p-id="2451"
fill="#ffffff"
></path>
</svg>
</template>
\ No newline at end of file
export const apiUrl = {
TEST_PROXY: '/api/newsqa/v1/query/inner/publish/modules/list'
};
\ No newline at end of file
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
<script setup lang="ts">
import { ref, reactive, toRefs } from "vue";
//底部tab栏相关
const useTabBar = () => {
const state = reactive({
tabBar: [
{
title: "首页",
to: {
name: "Home",
},
icon: "home-o",
},
{
title: "我",
to: {
name: "About",
},
icon: "user-o",
},
],
});
return toRefs(state);
};
const { tabBar } = useTabBar();
const handleChange = (value) => {
// console.log(value,'valueeeeeee');
};
</script>
<template>
<div class="app-container">
<div class="layout-content">
<keep-alive v-if="$route.meta.keepAlive">
<router-view></router-view>
</keep-alive>
<router-view v-else></router-view>
<RequestLoading></RequestLoading>
</div>
<div class="layout-footer">
<TabBar :data="tabBar" @chang="handleChange"></TabBar>
</div>
</div>
</template>
<style lang="scss" scoped>
</style>
\ No newline at end of file
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { vuexStore, piniaStore } from './store';
// 移动端适配
import 'lib-flexible/flexible.js';
// 引入全局样式
import '@/assets/scss/index.scss';
// 全局引入按需引入UI库 vant
import { vantPlugins } from './plugins/vant.js';
//全局公共组件
import components from './plugins/components.js';
createApp(App).use(vuexStore).use(piniaStore).use(router).use(vantPlugins).use(components).mount('#app');
\ No newline at end of file
const modules = import.meta.globEager('../components/*/*.vue');
export default {
install(app) {
Object.keys(modules).forEach(componentPath => {
// 获取遍历的当前组件实例对象
let curComponent = modules[componentPath]?.default;
app.component(curComponent.name, curComponent);
});
}
}
/**
* @author TalkTao
* @description 按需引入Vant
*/
import { Button, Tabbar, TabbarItem, Sticky, NavBar, Icon, Search, DropdownMenu, DropdownItem, Image, Lazyload, Tabs, Tab, Toast, Field, Cell, CellGroup, Form, List } from 'vant'
const pluginsVant = [
Button,
Tabbar,
TabbarItem,
Sticky,
NavBar,
Icon,
Search,
DropdownMenu,
DropdownItem,
Image,
Lazyload,
Tabs,
Tab,
Toast,
Field,
Cell,
CellGroup,
Form,
List
]
export const vantPlugins = {
install: function(vm) {
pluginsVant.forEach((item:any) => {
vm.component(item.name, item);
});
}
};
\ No newline at end of file
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
// 通过Vite的import.meta.glob()方法实现自动化导入路由
const mainRouterModules = import.meta.glob('../layout/*.vue')
const viewRouterModules = import.meta.glob('../views/**/*.vue')
// 子路由
const childRoutes = Object.keys(viewRouterModules).map((path)=>{
const childName = path.match(/\.\.\/views\/(.*)\.vue$/)[1].split('/')[1];
return {
path: `/${childName.toLowerCase()}`,
name: childName,
component: viewRouterModules[path]
}
})
// 根路由
const rootRoutes = Object.keys(mainRouterModules).map((path) => {
const name = path.match(/\.\.\/layout\/(.*)\.vue$/)[1].toLowerCase();
const routePath = `/${name}`;
if (routePath === '/index') {
return {
path: '/',
name,
redirect: '/home',
component: mainRouterModules[path],
children: childRoutes
};
}
})
const routes: Array<RouteRecordRaw> = rootRoutes
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export default router
import { createStore } from "vuex";
import { createPinia } from "pinia";
export const vuexStore = createStore({
state: {
isLoading: false,
userName: ""
},
getters: {
},
mutations: {
changeIsLoading(state, val) {
state.isLoading = val;
},
getUserNmae(state, data) {
state.userName = data;
}
},
actions: {
},
modules: {},
});
export const piniaStore = createPinia();
import { ref } from 'vue'
import { defineStore } from "pinia"
export const usePiniaState = defineStore('pinia', ()=>{
const userName = ref('')
const getUserNmae = (data) => {
userName.value = data
}
return { userName, getUserNmae}
})
// export const usePiniaState = defineStore({
// id: 'textPinia',
// state: () => {
// return {
// userName: ''
// }
// },
// getters: {
// },
// actions: {
// getUserNmae(data) {
// this.userName = data
// }
// }
// })
\ No newline at end of file
/**
* @author TalkTao
* @description 接口封装
*/
import { vuexStore } from "@/store/index";
import axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse, Method } from "axios";
export interface AxiosResponseProps {
code?: number;
status?: number;
data?: any;
datas?: any;
msg?: string | null;
}
class HttpRequest {
private readonly baseURL: string;
private readonly withCredentials: boolean;
private readonly timeout: number;
constructor() {
//获取当前环境的api地址
this.baseURL = import.meta.env.VITE_BASE_URL as string;
this.withCredentials = true;
this.timeout = 1000 * 60;
}
//初始化get请求
getInitConfig(): AxiosRequestConfig {
return {
baseURL: this.baseURL,
withCredentials: this.withCredentials,
timeout: this.timeout,
};
}
interceptors(instance: AxiosInstance) {
// 定义存放请求接口数组
let requestList = [];
const setLoadingToFalse = response => {
requestList
.filter(item => item.url == response.config.url && item.method == response.config.method)
.forEach(item => (item.isLoading = false));
//所有请求都加载完才让加载提示消失
if (requestList.every(item => !item.isLoading)) vuexStore.commit("changeIsLoading", false);
};
instance.interceptors.request.use(
(config: any) => {
// 不用判断请求loading的路由
let ignoreLoadingUrls = ["/login"];
if (!ignoreLoadingUrls.includes(config.url)) {
if (requestList.length < 10) {
requestList.unshift({ ...config, isLoading: true });
} else {
requestList = [...requestList.slice(0, 9), { ...config, isLoading: true }];
}
vuexStore.commit("changeIsLoading", true);
}
return config;
},
error => Promise.reject(error + '请求错误')
);
instance.interceptors.response.use(
response => {
setLoadingToFalse(response);
return response.data;
},
error => {
if (error.response.status == 301) { }
setLoadingToFalse(error);
return Promise.reject(error.response?.data);
}
);
}
request(): AxiosInstance {
const instance = axios.create(this.getInitConfig());
this.interceptors(instance);
return instance;
}
}
const http = new HttpRequest();
export default http.request();
<script setup lang="ts">
import { computed } from 'vue'
import { useStore } from 'vuex'
import { storeToRefs } from 'pinia'
import { usePiniaState } from '@/store/testPinia'
import logo from '@/assets/logo.png'
// vuex
// const vuexStore = useStore()
// pinia
const piniaStore = usePiniaState()
// vuex
// const userName = computed(() => vuexStore.state.userNmae)
// 通过storeToRefs方法将存储在pinia里的数据解构出来,保持state响应性
const { userName } = storeToRefs(piniaStore)
const { getUserNmae } = piniaStore
const handleBtn = () =>{
// vuex
// vuexStore.commit('userName','真乖,如果对您有帮助请在github上点个星星哦~')
// pinia
getUserNmae('真乖,如果对您有帮助请在github上点个星星哦~')
}
</script>
<template>
<div class="about">
<CustomHeader title="我的" />
<div class="wrapper">
<div class="list flex-center py-8 flex-column">
<span class="logo">
<van-image width="150" :src="logo" />
</span>
<span class="logo fs-1 py-3 opacity-75">VUE3 H5开发模板</span>
<span class="fs-6 py-3 opacity-75">
项目地址:
<a href="https://github.com/talktao/Vue3-Vite-Vant-TS-H5">https://github.com/talktao/Vue3-Vite-Vant-TS-H5</a>
</span>
<span class="fs-3 py-3 opacity-75">项目作者:talktao</span>
<span class="fs-3 py-3 opacity-75">
{{ userName }}
<van-button v-if="userName == ''" color="#f50" size="small" @click="handleBtn">点我有魔法~</van-button>
</span>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<script setup lang="ts">
import { computed, onMounted, reactive, ref, toRefs } from "vue";
import { getTest } from "@/api/home";
import logo from "@/assets/logo.png";
const useShowList = () => {
const state = reactive({
list: [
{ title: "vite" },
{ title: "rem移动端适配" },
{ title: "VantUI 组件按需加载" },
{ title: "Sass 全局样式" },
{ title: "Vuex 状态管理" },
{ title: "Pinia 状态管理" },
{ title: "Axios 封装及接口管理" },
{ title: "Vue-router" },
{ title: "vite.config.ts 基础配置" },
{ title: "检查文件中的env路径" },
{ title: "配置 proxy 跨域" },
{ title: "配置 alias 别名" },
{ title: "Eslint+Pettier 统一开发规范" },
{ title: "批量全局注册公共组件" },
],
});
return toRefs(state);
};
const { list } = useShowList();
// 请求真实数据
const getTestData = async () => {
let params = {
modules: "statisGradeCityDetail",
};
const result = await getTest(params);
};
onMounted(() => {
getTestData();
});
</script>
<template>
<div>
<CustomHeader title="首页" />
<div class="py-3 px-3">
<div class="title py-2 flex-start align-items-center">
<van-image width="36" :src="logo" />
<span class="fs-1 px-5">Vue3+TS H5开发模板</span>
</div>
<div class="subTitle px-3 fs-3 opacity-50">
A Vue3 h5 template with Vant UI
</div>
</div>
<div class="py-5">
<van-list finished-text="没有更多了">
<van-cell
v-for="(item, index) in list"
:key="index"
:title="item.title"
icon="success"
/>
</van-list>
</div>
</div>
</template>
<style lang="scss" scoped>
h1 {
display: block;
font-size: 40px;
text-align: center;
padding: 20px 0;
}
ul {
li {
display: block;
font-size: 20px;
padding: 20px 0;
text-align: center;
}
}
</style>
\ No newline at end of file
<script setup lang="ts">
import { ref, reactive, toRefs } from 'vue'
// 搜索相关
const searchState = () => {
const state = reactive({
value: '', // 搜索input框的值
isSearch: true, // 是否显示历史搜索
goodsList: [], // 根据搜索关键字返回的商品数据
titleText: '搜索',
scrollTop: 0 // 获取滚动高度
})
return toRefs(state)
}
const {value, isSearch, goodsList, titleText, scrollTop} = searchState()
const active = ref(0) // tab切换默认值
let testData = [
{id:1,title:'四件套', price: 999},
{id:2,title:'被子', price: 555},
{id:3,title:'枕头', price: 888},
]
// 获取搜索成功的数据
const getSearchResult = (params) => {
console.log(params,'params');
// 接口
}
// 点击搜索的回调
const handleSearch = (e) => {
getSearchResult(e)
console.log(e,'eeeeeehandleSearch');
}
// 切换tab分类标签的回调
const handleTab = (e) => {
// getSearchResult({keywords:value.value,searchValue: e.name})
// console.log(e,'etabs');
switch (e.name) {
case 0:
testData = testData
console.log(testData,'000000000');
break;
case 1:
testData = testData
console.log(testData,'111111111');
break;
case 2:
testData = testData.sort((a,b) => a.price - b.price)
console.log(testData,'222222222');
break;
case 3:
testData = testData.sort((a,b) => b.price - a.price)
console.log(testData,'33333333333');
break;
default:
break;
}
}
</script>
<template>
<div class="search d-flex flex-column justify-content-start">
<!-- 头部搜索 -->
<van-sticky>
<CustomHeader title="搜索" leftIcon="arrow-left" leftText=""/>
<form action="javascript:return true">
<van-search
ref="search"
@focus="isSearch = true"
placeholder="请输入搜索文字"
shape="round"
v-model="value"
show-action
@click="isSearch = true"
@search="handleSearch(value)"
:autofocus="false"
>
<template #action>
<div class="search_Btn" @click="handleSearch(value)">搜索</div>
</template>
</van-search>
</form>
<van-tabs v-model:active="active" @click-tab="handleTab">
<van-tab title="综合"></van-tab>
<van-tab title="销量"></van-tab>
<van-tab title="价格升序"></van-tab>
<van-tab title="价格降序"></van-tab>
</van-tabs>
</van-sticky>
<!-- 商品列表展示 -->
<div class="card_Box mrn d-flex flex-column justify-content-start">
<div v-for="item in testData" :key="item.id">
<div class="list_content d-flex justify-content-between align-items-center">
<div class="left">
<van-image radius="14" width="130" height='120' fit="cover" src="https://img.yzcdn.cn/vant/cat.jpeg" >
<template v-slot:loading>
<van-loading type="spinner" size="20" />
</template>
<template v-slot:error>
<van-image></van-image>
</template>
</van-image>
</div>
<div class="right d-flex flex-column justify-content-around w-100 h-100 ps-4 fs-4">
<div>{{item.title}}</div>
<div>副标题xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</div>
<div class="price">价格:¥{{item.price}}</div>
<div>{{item.id}}</div>
</div>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.search {
overflow: hidden;
.search_Box {
padding: 4px 10px;
position: relative;
display: flex;
background-color: white;
>div:nth-child(1){
width: 1rem;
display: flex;
justify-content: center;
align-items: center;
color: #5f656b;
font-size: 20px;
}
>div:nth-child(2) {
width: calc(100% - 8vw);
.search_Btn {
color: #b91c3b;
}
}
}
.mrn {
width: 100vw;
height: 100vh;
>div:nth-child(1){
margin-top: 10px;
}
.list_content{
height: 32vw;
/* background-color: #f5f5f5; */
border-radius: 10px;
margin: 5px;
padding: 0 10px;
.left {
}
.right {
overflow: hidden;
>div{
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.price{
color: red;
}
}
}
}
}
</style>
\ No newline at end of file
{
"compilerOptions": {
"types": ["node"],
"target": "esnext",
"module": "esnext",
"strict": false,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx",
"src/**/*.vue",
"tests/**/*.ts",
"tests/**/*.tsx"
],
"exclude": [
"node_modules"
]
}
{
"compilerOptions": {
"composite": true,
"module": "esnext",
"moduleResolution": "node"
},
"include": ["vite.config.ts"]
}
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import path from "path";
import styleImport, { VantResolve } from 'vite-plugin-style-import';
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
// 检查process.cwd()路径下.env.develeport.local、.env.development、.env.local、.env这四个环境文件
loadEnv(mode, process.cwd());
return {
// 静态资源基础路径 base: './' || '',
base: '',
resolve: {
alias: {
// 配置src目录
"@": path.resolve(__dirname, "src"),
// 导入其他目录
"components": path.resolve(__dirname, "components")
}
},
plugins: [
vue(),
// 配置后,Vant各组件才生效
styleImport({
resolves: [VantResolve()],
}),
],
// 跨域代理
server: {
host: '0.0.0.0',
proxy: {
'/api': {
target: 'https://api.inews.qq.com',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') // 将匹配到的api替换成''
}
}
}
};
});
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论