vuex和pinia

master
barney 2 years ago
parent 6e0f4107f1
commit a36de53202
  1. 3
      .gitignore
  2. 3
      Vite-Vue-tutorial/package.json
  3. 8
      Vite-Vue-tutorial/src/.prettierrc
  4. 18
      Vite-Vue-tutorial/src/hooks/useCategories.js
  5. 84
      Vite-Vue-tutorial/src/hooks/useProducts.js
  6. 4
      Vite-Vue-tutorial/src/services/banners.js
  7. 4
      Vite-Vue-tutorial/src/services/categories.js
  8. 20
      Vite-Vue-tutorial/src/services/products.js
  9. 74
      Vite-Vue-tutorial/src/utils/request.js
  10. 21
      Vite-Vue-tutorial/src/utils/tools.js
  11. 40
      Vite-Vue-tutorial/src/views/Detail.vue
  12. 20
      Vite-Vue-tutorial/src/views/Home.vue
  13. 102
      Vite-Vue-tutorial/src/views/List.vue
  14. 24
      pinia-pre/.gitignore
  15. 3
      pinia-pre/.vscode/extensions.json
  16. 16
      pinia-pre/README.md
  17. 13
      pinia-pre/index.html
  18. 1492
      pinia-pre/package-lock.json
  19. 21
      pinia-pre/package.json
  20. 1
      pinia-pre/public/vite.svg
  21. 40
      pinia-pre/src/App.vue
  22. 1
      pinia-pre/src/assets/vue.svg
  23. 12
      pinia-pre/src/components/HelloWorld.vue
  24. 8
      pinia-pre/src/main.ts
  25. 47
      pinia-pre/src/stores/counter.ts
  26. 0
      pinia-pre/src/style.css
  27. 7
      pinia-pre/src/vite-env.d.ts
  28. 18
      pinia-pre/tsconfig.json
  29. 9
      pinia-pre/tsconfig.node.json
  30. 7
      pinia-pre/vite.config.ts
  31. 4
      vite-ts-project/src/views/List.vue
  32. 8
      vite-ts-project/src/views/Login.vue
  33. 14
      vite-ts-project/src/views/User.vue
  34. 24
      vuex-app/.gitignore
  35. 3
      vuex-app/.vscode/extensions.json
  36. 7
      vuex-app/README.md
  37. 13
      vuex-app/index.html
  38. 1355
      vuex-app/package-lock.json
  39. 21
      vuex-app/package.json
  40. 1
      vuex-app/public/vite.svg
  41. 26
      vuex-app/src/App.vue
  42. 1
      vuex-app/src/assets/vue.svg
  43. 5
      vuex-app/src/components/HelloWorld.vue
  44. 7
      vuex-app/src/main.js
  45. 20
      vuex-app/src/store/index.js
  46. 90
      vuex-app/src/style.css
  47. 7
      vuex-app/vite.config.js

3
.gitignore vendored

@ -1,3 +1,6 @@
# vscode-workspace
*.code-workspace
# Logs
logs
*.log

@ -6,7 +6,8 @@
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
"preview": "vite preview",
"format": "prettier --write ./**/*.{html,vue,ts,js,json}"
},
"dependencies": {
"axios": "^0.27.2",

@ -1,4 +1,6 @@
{
"singleQuote": false,
"semi": true
}
"singleQuote": false,
"semi": true,
"tabWidth": 4,
"useTabs": true
}

@ -3,15 +3,15 @@ import { ref } from "vue";
/**
* 返回分类接口的数据
* @returns
* @returns
*/
export const useCategories = () => {
const categories = ref([]);
loadCategoriesAPI().then(res => {
categories.value = res.data;
});
return {
categories,
}
};
const categories = ref([]);
loadCategoriesAPI().then((res) => {
categories.value = res.data;
});
return {
categories,
};
};

@ -1,53 +1,51 @@
import { loadProductAPI } from '../services/products';
import { loadProductAPI } from "../services/products";
import { ref } from "vue";
/**
* 返回商品接口的数据
* @param {*} categoryId 当前的商品分类id, 空表示获取所有分类
* @returns
* @returns
*/
export const useProducts = (categoryId='') => {
export const useProducts = (categoryId = "") => {
const page = ref(1); // 当前页码
const loading = ref(false); // 是否在加载中
const finished = ref(false); // 是否加载完成
const currentCategoryId = ref(categoryId); // 当前分类id
const products = ref([]); // 商品数据
const page = ref(1); // 当前页码
const loading = ref(false); // 是否在加载中
const finished = ref(false); // 是否加载完成
const currentCategoryId = ref(categoryId); // 当前分类id
const products = ref([]); // 商品数据
/**
* 加载products
* @param {*} needReset 是否需要重置products
* @param {*} categoryId 分类id
*/
const onLoad = (needReset = false, categoryId = "") => {
if (needReset) {
// 重置一些参数
finished.value = false;
products.value = [];
page.value = 1;
}
// 分类id不为空才需要修改
if (categoryId !== "") {
currentCategoryId.value = categoryId;
}
loading.value = true; // 开始加载
loadProductAPI(page.value, currentCategoryId.value).then((res) => {
finished.value = page.value > res.pages; // 当前页码超过总页数时表示加载完成
loading.value = false;
// 将获取的数据存入products
products.value.push(...res.data);
page.value++; // 页码+1
});
};
/**
* 加载products
* @param {*} needReset 是否需要重置products
* @param {*} categoryId 分类id
*/
const onLoad = (needReset=false,categoryId='') => {
if (needReset) {
// 重置一些参数
finished.value = false;
products.value = [];
page.value = 1;
}
// 分类id不为空才需要修改
if (categoryId !=='' ) {
currentCategoryId.value = categoryId;
}
loading.value = true; // 开始加载
loadProductAPI(page.value, currentCategoryId.value).then((res) => {
finished.value = page.value > res.pages; // 当前页码超过总页数时表示加载完成
loading.value = false;
// 将获取的数据存入products
products.value.push(...res.data);
page.value++; // 页码+1
});
};
return {
// page,
loading,
finished,
products,
currentCategoryId,
onLoad,
}
return {
// page,
loading,
finished,
products,
currentCategoryId,
onLoad,
};
};

@ -2,6 +2,6 @@ import { get } from "../utils/request";
/**
* 获取轮播图
* @returns
* @returns
*/
export const loadBannersAPI = () => get('/api/v1/banners');
export const loadBannersAPI = () => get("/api/v1/banners");

@ -2,6 +2,6 @@ import { get } from "../utils/request";
/**
* 返回所有商品分类
* @returns
* @returns
*/
export const loadCategoriesAPI = () => get('api/v1/product_categories');
export const loadCategoriesAPI = () => get("api/v1/product_categories");

@ -1,21 +1,21 @@
import { get } from '../utils/request'
import { get } from "../utils/request";
/**
* 获取商品信息
* @param {*} page 页号
* @param {*} category 分类为空表示不分类
* @returns
* @returns
*/
export const loadProductAPI = (page = 1, category = '') => get('/api/v1/products/', {
page,
category,
// per, 每页最多多少数据
});
export const loadProductAPI = (page = 1, category = "") =>
get("/api/v1/products/", {
page,
category,
// per, 每页最多多少数据
});
/**
* 返回商品详情
* @param {*} id 商品id
* @returns
* @returns
*/
export const loadProductByIdAPI = id => get('/api/v1/products/'+ id);
export const loadProductByIdAPI = (id) => get("/api/v1/products/" + id);

@ -1,13 +1,13 @@
import axios from "axios"
import NProgress from "nprogress"
import "nprogress/nprogress.css"
import axios from "axios";
import NProgress from "nprogress";
import "nprogress/nprogress.css";
export const serverUrl = "http://localhost:1337"
export const serverUrl = "http://localhost:1337";
const instance = axios.create({
baseURL: serverUrl, // baseURL会在发送请求的时候拼接在url参数的前面
timeout: 5000, // 设置超时时间
})
baseURL: serverUrl, // baseURL会在发送请求的时候拼接在url参数的前面
timeout: 5000, // 设置超时时间
});
/**
* get 请求
@ -17,9 +17,9 @@ const instance = axios.create({
*/
// axios.get(url, config)请求第二个参数表示配置信息(包括请求数据、请求头等)
export const get = (url, params) =>
instance.get(url, {
params: params, // url传递的参数
})
instance.get(url, {
params: params, // url传递的参数
});
/**
* post 请求
@ -28,7 +28,7 @@ export const get = (url, params) =>
* @returns
*/
// axios.post(url, data, config)请求第三个参数表示配置信息(包括请求数据、请求头等)
export const post = (url, data) => instance.post(url, data)
export const post = (url, data) => instance.post(url, data);
/**
* put 请求
@ -36,41 +36,41 @@ export const post = (url, data) => instance.post(url, data)
* @param {*} data 数据
* @returns
*/
export const put = (url, data) => instance.put(url, data)
export const put = (url, data) => instance.put(url, data);
/**
* delete 请求
* @param {*} url 请求地址
* @returns
*/
export const del = (url) => instance.delete(url)
export const del = (url) => instance.delete(url);
// Add a request interceptor (全局请求拦截)
instance.interceptors.request.use(
function (config) {
// console.group("全局请求拦截");
// console.log(config);
// console.groupEnd();
NProgress.start() // 启动进度条
// 这里还可以设置token
return config
},
function (error) {
return Promise.reject(error)
}
)
function (config) {
// console.group("全局请求拦截");
// console.log(config);
// console.groupEnd();
NProgress.start(); // 启动进度条
// 这里还可以设置token
return config;
},
function (error) {
return Promise.reject(error);
}
);
// Add a response interceptor (全局响应拦截)
instance.interceptors.response.use(
function (response) {
// console.group("全局响应拦截");
// console.log(response);
// console.groupEnd();
NProgress.done() // 关闭进度条
return response.data
},
function (error) {
NProgress.done() // 关闭进度条
return Promise.reject(error)
}
)
function (response) {
// console.group("全局响应拦截");
// console.log(response);
// console.groupEnd();
NProgress.done(); // 关闭进度条
return response.data;
},
function (error) {
NProgress.done(); // 关闭进度条
return Promise.reject(error);
}
);

@ -1,18 +1,17 @@
import { serverUrl } from "./request";
export const extName = str => `${str}`;
export const extName = (str) => `${str}`;
/**
* 处理返回数据中的url字段
* @param {*} url 请求返回的图片url
*/
export const dalImageUrl = url => {
if (url) {
if (url.startsWith('http')) {
return url;
}
return serverUrl + url;
}
return "https://hugo.bnblogs.cc/images/img/20220215001349.png";
}
export const dalImageUrl = (url) => {
if (url) {
if (url.startsWith("http")) {
return url;
}
return serverUrl + url;
}
return "https://hugo.bnblogs.cc/images/img/20220215001349.png";
};

@ -1,21 +1,25 @@
<template>
<div class="detail">
<nav-bar
:title="product.name"
left-text="返回"
left-arrow
@click-left="onClickLeft"
/>
<div class="detail">
<nav-bar
:title="product.name"
left-text="返回"
left-arrow
@click-left="onClickLeft"
/>
<h1>{{ product.name }}</h1>
<div class="content" v-html="product.content"></div>
<action-bar>
<action-bar-icon icon="chat-o" text="客服" @click="onClickIcon" />
<action-bar-icon icon="cart-o" text="购物车" @click="onClickIcon" />
<action-bar-icon icon="shop-o" text="店铺" @click="onClickIcon" />
<action-bar-button type="danger" text="立即购买" @click="onClickButton" />
</action-bar>
</div>
<h1>{{ product.name }}</h1>
<div class="content" v-html="product.content"></div>
<action-bar>
<action-bar-icon icon="chat-o" text="客服" @click="onClickIcon" />
<action-bar-icon icon="cart-o" text="购物车" @click="onClickIcon" />
<action-bar-icon icon="shop-o" text="店铺" @click="onClickIcon" />
<action-bar-button
type="danger"
text="立即购买"
@click="onClickButton"
/>
</action-bar>
</div>
</template>
<script setup>
@ -30,11 +34,11 @@ const router = useRouter();
const product = ref({});
loadProductByIdAPI(route.query.id).then((res) => {
product.value = res.data;
product.value = res.data;
});
const onClickIcon = () => {};
const onClickButton = () => {};
const onClickLeft = () => {
router.go(-1); //
router.go(-1); //
};
</script>

@ -21,7 +21,7 @@
:price="item.price.toFixed(2)"
:title="item.name"
:thumb="dalImageUrl(item.coverImage)"
@click-thumb="toDetail(item.id)"
@click-thumb="toDetail(item.id)"
/>
</div>
</template>
@ -38,7 +38,7 @@ import { useCategories } from "../hooks/useCategories";
import { useProducts } from "../hooks/useProducts";
//
const { products, onLoad} = useProducts();
const { products, onLoad } = useProducts();
onLoad();
//
@ -54,14 +54,14 @@ const { categories } = useCategories();
//
const router = useRouter();
const toDetail = id => {
router.push({
name: 'Detail',
query: {
id,
}
})
}
const toDetail = (id) => {
router.push({
name: "Detail",
query: {
id,
},
});
};
</script>
<style scoped></style>

@ -1,32 +1,32 @@
<template>
<div class="list">
<sidebar v-model="active">
<sidebar-item
v-for="item in categories"
:title="item.name"
:key="item.id"
:to="{ name: 'List', query: { tid: item.id } }"
/>
</sidebar>
<!-- @load="onLoad": 当loading和finished同时为false时执行-->
<list
class="products"
:loading="loading"
:finished="finished"
@load="onLoad"
finished-text="没有更多了"
>
<card
v-for="item in products"
:key="item.id"
:num="item.amount"
:price="item.price.toFixed(2)"
:title="item.name"
:thumb="dalImageUrl(item.coverImage)"
@click-thumb="toDetail(item.id)"
/>
</list>
</div>
<div class="list">
<sidebar v-model="active">
<sidebar-item
v-for="item in categories"
:title="item.name"
:key="item.id"
:to="{ name: 'List', query: { tid: item.id } }"
/>
</sidebar>
<!-- @load="onLoad": 当loading和finished同时为false时执行-->
<list
class="products"
:loading="loading"
:finished="finished"
@load="onLoad"
finished-text="没有更多了"
>
<card
v-for="item in products"
:key="item.id"
:num="item.amount"
:price="item.price.toFixed(2)"
:title="item.name"
:thumb="dalImageUrl(item.coverImage)"
@click-thumb="toDetail(item.id)"
/>
</list>
</div>
</template>
<script setup>
@ -39,39 +39,39 @@ import { useProducts } from "../hooks/useProducts";
const route = useRoute();
const router = useRouter();
const { loading, finished, products, currentCategoryId, onLoad } = useProducts(
route.query.tid
route.query.tid
);
//
const { categories } = useCategories();
//
const active = computed({
//
set(v) {
return v;
},
//
get() {
return categories.value.findIndex(
(item) => item.id == currentCategoryId.value
);
},
//
set(v) {
return v;
},
//
get() {
return categories.value.findIndex(
(item) => item.id == currentCategoryId.value
);
},
});
// ()
onBeforeRouteUpdate((to, from) => {
// currentCategoryId.value = to.query.tid;
//
onLoad(true, to.query.tid);
// currentCategoryId.value = to.query.tid;
//
onLoad(true, to.query.tid);
});
const toDetail = (id) => {
router.push({
name: "Detail",
query: {
id,
},
});
router.push({
name: "Detail",
query: {
id,
},
});
};
// loadDataFromServer();
@ -79,14 +79,14 @@ const toDetail = (id) => {
<style scoped>
.list {
display: flex;
display: flex;
}
.list .van-sidebar {
width: 105px;
width: 105px;
}
.products {
overflow: auto;
overflow: auto;
}
</style>

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

@ -0,0 +1,16 @@
# Vue 3 + TypeScript + Vite
This template should help get you started developing with Vue 3 and TypeScript in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)
## Type Support For `.vue` Imports in TS
Since TypeScript cannot handle type information for `.vue` imports, they are shimmed to be a generic Vue component type by default. In most cases this is fine if you don't really care about component prop types outside of templates. However, if you wish to get actual prop types in `.vue` imports (for example to get props validation when using manual `h(...)` calls), you can enable Volar's Take Over mode by following these steps:
1. Run `Extensions: Show Built-in Extensions` from VS Code's command palette, look for `TypeScript and JavaScript Language Features`, then right click and select `Disable (Workspace)`. By default, Take Over mode will enable itself if the default TypeScript extension is disabled.
2. Reload the VS Code window by running `Developer: Reload Window` from the command palette.
You can learn more about Take Over mode [here](https://github.com/johnsoncodehk/volar/discussions/471).

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue + TS</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,21 @@
{
"name": "pinia-pre",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc --noEmit && vite build",
"preview": "vite preview"
},
"dependencies": {
"pinia": "^2.0.22",
"vue": "^3.2.37"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.1.0",
"typescript": "^4.6.4",
"vite": "^3.1.0",
"vue-tsc": "^0.40.4"
}
}

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,40 @@
<template>
<h1>Hello</h1>
<hr />
<button @click="onHandleSub">-</button>
<span>{{ count }}</span>
<button @click="onHandlePlus">+</button>
<div class="padding-10"></div>
<button @click="onHandleAsyncPlus">异步+</button>
<HW />
</template>
<script setup lang="ts">
import { storeToRefs } from "pinia";
import useCounterStore from "./stores/counter";
import HW from "./components/HelloWorld.vue";
const store = useCounterStore();
const { count } = storeToRefs(store);
const onHandlePlus = () => {
store.plus();
};
const onHandleSub = () => {
store.sub();
};
const onHandleAsyncPlus = () => {
store.asyncPlus();
};
</script>
<style scoped>
span {
font-size: 20px;
padding: 0 15px;
}
.padding-10 {
padding-top: 10px;
}
</style>

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

@ -0,0 +1,12 @@
<script setup lang="ts">
import { storeToRefs } from "pinia";
import useCounterStore from "../stores/counter";
const store = useCounterStore();
const {count} = storeToRefs(store);
</script>
<template>
<h3>这是Hello World组件数据: {{ count }}</h3>
</template>
<style scoped></style>

@ -0,0 +1,8 @@
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { createPinia } from 'pinia'
const pinia = createPinia();
createApp(App).use(pinia).mount('#app')

@ -0,0 +1,47 @@
import { defineStore } from "pinia";
import { ref } from 'vue'
// 选项式api的写法
// export default defineStore('counter',{
// // 定义数据
// state: () => {
// return {
// count: 1,
// title: '标题',
// }
// },
// // 数据操作
// actions: {
// plus() {
// this.count++;
// },
// sub() {
// this.count--;
// },
// asyncPlus() {
// setTimeout(() => {
// this.count++;
// }, 1000);
// }
// }
// })
// 组合式api写法
export default defineStore('counter', () => {
const count = ref(0);
const plus = () => {
count.value ++;
};
const sub = () => {
count.value --;
};
const asyncPlus = () => {
setTimeout(() => {
count.value++;
}, 1000);
};
return {
count,plus,sub,asyncPlus,
}
});

@ -0,0 +1,7 @@
/// <reference types="vite/client" />
declare module '*.vue' {
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}

@ -0,0 +1,18 @@
{
"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
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"],
"references": [{ "path": "./tsconfig.node.json" }]
}

@ -0,0 +1,9 @@
{
"compilerOptions": {
"composite": true,
"module": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true
},
"include": ["vite.config.ts"]
}

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})

@ -2,13 +2,13 @@
<h1>列表</h1>
<hr />
<div class="list">
<router-link
:to="{ name: 'Detail', query: { id: item.id } }"
v-for="item in courses"
:key="item.id"
><li>{{ item.name }}</li></router-link
>
<li>{{ item.name }}</li>
</router-link>
</div>
</template>

@ -4,13 +4,13 @@
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router';
import { useRouter } from "vue-router";
const { replace } = useRouter();
const login = () => {
localStorage.setItem("token", "1111");
replace({
name: 'User',
})
replace({
name: "User",
});
};
</script>

@ -1,8 +1,8 @@
<template>
<h1>用户页</h1>
<router-link :to="{name: 'userInfo'}">个人信息</router-link>
<router-link :to="{name: 'userScores'}">我的订单</router-link>
<router-link :to="{name: 'userOrders'}">我的积分</router-link>
<hr />
<router-view></router-view>
</template>
<h1>用户页</h1>
<router-link :to="{ name: 'userInfo' }">个人信息</router-link>
<router-link :to="{ name: 'userScores' }">我的订单</router-link>
<router-link :to="{ name: 'userOrders' }">我的积分</router-link>
<hr />
<router-view></router-view>
</template>

@ -0,0 +1,24 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

@ -0,0 +1,3 @@
{
"recommendations": ["Vue.volar"]
}

@ -0,0 +1,7 @@
# Vue 3 + Vite
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
## Recommended IDE Setup
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar)

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + Vue</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

@ -0,0 +1,21 @@
{
"name": "vuex-app",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
},
"dependencies": {
"router": "^1.3.7",
"vue": "^3.2.37",
"vue-router": "^4.1.5",
"vuex": "^4.0.2"
},
"devDependencies": {
"@vitejs/plugin-vue": "^3.1.0",
"vite": "^3.1.0"
}
}

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

@ -0,0 +1,26 @@
<script setup>
import { useStore } from "vuex";
const store = useStore();
const handlePlus = () => {
store.commit("increment");
};
const handleSub = () => {
store.commit("subtraction");
};
</script>
<template>
<h3>{{ store.state.title }}</h3>
<button @click="handleSub">-</button>
<span>{{ store.state.count }}</span>
<button @click="handlePlus">+</button>
</template>
<style scoped>
span {
font-size: 20px;
padding: 0 10px;
font-weight: 600;
}
</style>

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="37.07" height="36" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 198"><path fill="#41B883" d="M204.8 0H256L128 220.8L0 0h97.92L128 51.2L157.44 0h47.36Z"></path><path fill="#41B883" d="m0 0l128 220.8L256 0h-51.2L128 132.48L50.56 0H0Z"></path><path fill="#35495E" d="M50.56 0L128 133.12L204.8 0h-47.36L128 51.2L97.92 0H50.56Z"></path></svg>

After

Width:  |  Height:  |  Size: 496 B

@ -0,0 +1,5 @@
<script setup></script>
<template></template>
<style scoped></style>

@ -0,0 +1,7 @@
import { createApp } from "vue";
import "./style.css";
import App from "./App.vue";
import store from "./store";
createApp(App).use(store).mount("#app");

@ -0,0 +1,20 @@
import { createStore } from "vuex";
const store = createStore({
state() {
return {
count: 0,
title: "修改count值",
};
},
mutations: {
increment(state) {
state.count++;
},
subtraction(state) {
state.count--;
},
},
});
export default store;

@ -0,0 +1,90 @@
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}

@ -0,0 +1,7 @@
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [vue()]
})
Loading…
Cancel
Save