申请并汇入商品
vue create new-project(专案名称)
npm run serve
连接资料库用
VUE_APP_API=
VUE_APP_PATH=rouoxo
view > home.vue
export default {
name: "Home",
components: {
HelloWorld,
},
created() {
//连接资料库
console.log(process.env.VUE_APP_API);
console.log(process.env.VUE_APP_PATH);
},
};
https://bootstrap.hexschool.com/
npm install bootstrap@
https://bootstrap.hexschool.com/docs/4.2/getting-started/theming/
2-1 自定义 sass
App.vue
<style lang="scss">
@import "~bootstrap/scss/bootstrap";
</style>
npm run serve
2-2 客制化样式
assets 下新增 all.scss
assets 下新增 helpers资料夹 下新增 _variables.scss(_不会被辨识到)
并将原本的BS内 variables.scss 资料贴至 _variables.scss 内
2-3 自定义汇入
assets > all.scss
@import "~bootstrap/scss/functions";
@import "./helpers/variables";
@import "~bootstrap/scss/mixins";
@import "~bootstrap/scss/bootstrap";
App.vue
<style lang="scss">
@import "./assets/all";
</style>
多尝试几次
https://www.npmjs.com/package/vue-axios
npm install --save axios vue-axios
main.js
import axios from 'axios'
import VueAxios from 'vue-axios'
app.use(VueAxios, axios);
https://github.com/hexschool/vue3-course-api-wiki/wiki/%E8%AA%B2%E7%A8%8B%E9%83%A8%E5%88%86%E6%A8%A1%E6%9D%BF
View 新增 Login.vue
Router > index.js
{
path: "/login",
name: "Login",
component: () =>
import("../views/Login.vue"),
},
https://github.com/hexschool/vue3-course-api-wiki/wiki/%E7%99%BB%E5%85%A5%E5%8F%8A%E9%A9%97%E8%AD%89
登入成功显示范例
"uid": "XX4VbV87lRRBXKhZKT7YX6zhsuO2",
"token": "xxx"
"expired": "1234567890"
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
https://github.com/axios/axios#global-axios-defaults
document.cookie = "doSomethingOnlyOnce(名称自订)=true(值);
expires(到期日)=Fri,31 Dec 9999 23:59:59 GMT; SameSite=None; Secure";
目标:将token存进Cookie
const {token, expired} = res.data //token、expired = data内的token、expired
console.log(token ,expired)
document.cookie = `hexToken=${token}; expired=${new Date(expired)}`
成功
API
https://github.com/hexschool/vue3-course-api-wiki/wiki/%E7%99%BB%E5%85%A5%E5%8F%8A%E9%A9%97%E8%AD%89
https://developer.mozilla.org/zh-CN/docs/Web/API/Document/cookie
1-1 制作元素页面
Dashboard.vue
created() {
const token = document.cookie.replace(
// hexToken cookie名称
/(?:(?:^|.*;\s*)hexToken\s*=\s*([^;]*).*$)|^.*$/,
"$1"
);
console.log(token);
},
};
1-2 綑绑路由
index.js
{
path: "/dashboard",
name: "Dashboard",
component: () =>
import("../views/Dashboard.vue"),
},
https://github.com/axios/axios
Dashboard.vue
this.$http.default.headers.common["Authorization"] = token;
Dashboard.vue
https://github.com/hexschool/vue3-course-api-wiki/wiki/%E7%99%BB%E5%85%A5%E5%8F%8A%E9%A9%97%E8%AD%89
const api = `${process.env.VUE_APP_API}api/user/check`;
console.log(api);
this.$http.post(api, this.user).then((res) => {
console.log(res);
});
登入成功
如果把cookie的hexToken删除
登入失败
Login.vue如果登入成功转Dashboard.vue页面
this.$http.post(api, this.user).then((res) => {
// 如果登入成功转跳Dashboard.vue页面
if (res.data.success) {
this.$router.push("/dashboard");
}
});
如果登入失败Dashboard.vue会转回Login.vue
if (!res.data.success) {
this.$router.push("/login");
}
显示App.vue
<router-view />
删除
<router-link />
index.js
{
path: "/dashboard",
name: "Dashboard",
component: () => import("../views/Dashboard.vue"),
// 多个,阵列
children: [
{
path: "products",
component: () => import("../views/Products.vue"),
},
],
},
this.$router.push("/dashboard/products");
把Navbar拆出来做成一个小元件
7-1. 新增 components > Navbar.vue
并把Dashboard.vue 的 BS Navbar 剪下贴上
7-2 Dashboard.vue下注册该 Navbar.vue 元件 并放入
<Navber></Navber>
import Navber from "../components/Navbar.vue";
export default {
// 注册元件
components: {
Navber
},
https://github.com/hexschool/vue3-course-api-wiki/wiki/%E7%99%BB%E5%85%A5%E5%8F%8A%E9%A9%97%E8%AD%89
Navbar.vue
<a class="nav-link" href="#" v-on:click.prevent="logout">登出</a>
methods: {
logout() {
// 制作登出方法
const api = `${process.env.VUE_APP_API}logout`;
console.log(api);
// axios post(api, 欲传送的值)方法方法
this.$http.post(api, this.user).then((res) => {
//
if (res.data.success) {
console.log(res);
// 转跳登入页面
this.$router.push("/login");
}
});
},
},
成功,F12检查
data: {success: true, message: '已登出'}
完成图
<!-- 让版面不要太贴齐周边 BS样式 -->
<div class="container-fluid">
<router-view />
</div>
检查
目标:新增Bs Modal 弹跳视窗
https://bootstrap.hexschool.com/docs/4.2/components/modal/
BS的Modal,可以用JS呼叫出来使用~
components > ProductModal.vue
<ProductModal></ProductModal>
import ProductModal from "../components/ProductModal.vue";
components: {
// 区域注册元件
ProductModal,
},
Modal元件已经放入,剩下写判断式让按按钮後 Modal跳出
Products.vue
<!-- 使用元件 ref似id 之後可以用this.$refs抓 -->
<ProductModal ref="poductModal"></ProductModal>
<button
v-on:click="$refs.poductModal.showModal()"
class="btn btn-primary"
type="button"
>
增加一个产品
</button>
目标:弹出视窗的元件,输入完资料後,资料推进产品列表元件内
逻辑
API 取得商品列表
https://github.com/hexschool/vue3-course-api-wiki/wiki/%E7%AE%A1%E7%90%86%E6%8E%A7%E5%88%B6%E5%8F%B0-%5B%E9%9C%80%E9%A9%97%E8%AD%89%5D#%E5%95%86%E5%93%81%E5%BB%BA%E7%AB%8B
模板 增加 Bootstrap Modal
https://github.com/hexschool/vue3-course-api-wiki/wiki/%E8%AA%B2%E7%A8%8B%E9%83%A8%E5%88%86%E6%A8%A1%E6%9D%BF
openModal() {
this.tempProduct = {};
const productComponent = this.$refs.productModal;
productComponent.showModal();
},
emit内传外
ProductModal.vue
emit送出去
v-on:click="$emit('update-product',tempProduct)"
type="button" class="btn btn-primary">确认</button>
products.vue
接收
<ProductModal
v-on:update-product="updateProduct"
ref="productModal">
</ProductModal>
updateProduct(item) {
// 从内层(ProductModal.vue)接收资料, 送进tempProduct仓库
this.tempProduct = item;
this.$http.post(api, { data: this.tempProduct })
.then((res)=>{
// then 成功
console.log(res);
// 执行 ProductModal.vue 的 hideModal()
productComponent.hideModal();
// 执行
this.getProducts();
})
title(String)
category(String)
unit(String)
origin_price(Number)
price(Number) 为必填栏位
products.vue
data() {
return {
isNew: false,
};
true = 点此按钮时要新增商品
v-on:click="openModal(true)"
false = 点此按钮时非新增商品 = 编辑商品
item = v-if="item.is_enabled" 的 item
v-on:click="openModal(false,item)"
有够方便。
此时判断 新增商品 or 编辑商品
openModal(isNew, item) {
// if isNew = true
if(isNew){
// 新增商品
this.tempProduct ={};
}else{
// 编辑商品
// ... 展开旧的item
this.tempProduct ={...item};
}
updateProduct(item) {
// 从内层(ProductModal.vue)接收资料, 送进tempProduct仓库
this.tempProduct = item;
// 新增
// API使用方式 取得商品列表
let api = `${process.env.VUE_APP_API}api/${process.env.VUE_APP_PATH}/admin/product`;
let httpMethod = "post";
// 编辑
if (!this.isNew) {
api = `${process.env.VUE_APP_API}api/${process.env.VUE_APP_PATH}/admin/product/${item.id}`;
httpMethod = "put";
}
// this.$refs.productModal = html 内 <ProductModal ref="productModal"></ProductModal>
const productComponent = this.$refs.productModal;
this.$http[httpMethod](api, { data: this.tempProduct }).then((res) => {
// then 成功
console.log(res);
// 执行 ProductModal.vue 的 hideModal()
productComponent.hideModal();
// 执行 取得列表资料
this.getProducts();
});
},
为何使用[]?
如果 $http.httpMethod(xxxx) 会被判定成function
目标:点击新增产品内上传图片,可上传图片进资料库
取出档案,并改成FormData格式
ProductModal.vue
v-on:change="uploadfile"
ref="fileInput"
抓files[0]
![https://ithelp.ithome.com.tw/upload/images/20211115/20137684nviwLLUWkX.png](https://ithelp.ithome.com.tw/upload/images/20211115/20137684nviwLLUWkX.png)
uploadfile() {
// 抓取档案
// this.$refs.fileInput = ref="fileInput"
const uploadfile = this.$refs.fileInput.files[0];
console.dir(uploadfile);
// 制作成FormData格式
const formData = new FormData(); //Js
formData.append("file-to-upload", uploadfile); // "file-to-upload" API接收格式
},
uploadfile() {
//API收图片 表单传送 action
const url = `${process.env.VUE_APP_API}api/${process.env.VUE_APP_PATH}/admin/upload`;
// axios post(api, 欲传送的值)方法方法
this.$http.post(url, formData).then((response) => {
console.log(response.data);
if (response.data.success) {
this.tempProduct.imageUrl = response.data.imageUrl;
}
});
},
DelModal.vue
(1)src > 新增mixins 资料夹 > 新增 modalMixin.js
(2)把重复的资料放进去
(3)放到需要用到的元件内
import modalMixin from "@/mixins/modalMixin";
mixins: [modalMixin],
https://www.npmjs.com/package/vue3-loading-overlay
npm install vue3-loading-overlay
// 读取动画套件
// Import component
import Loading from "vue3-loading-overlay";
// Import stylesheet
import "vue3-loading-overlay/dist/vue3-loading-overlay.css";
// 全域注册
app.component('Loading', Loading)
Products.vue、Login.vue
active = true才会转圈圈
<Loading :active="isLoading"></Loading>
data() {
return {
// 判断是否要loading转圈圈 仓库
isLoading: false,
};
},
getProdcts() {
const api = `${process.env.VUE_APP_API}api/${process.env.VUE_APP_PATH}/admin/products`;
// 捞到资料前有loding
this.isLoading = true;
console.log(api);
this.$http.get(api).then((res) => {
// 捞到资料loding圈圈关闭
this.isLoading = false;
if (res.data.success) {
}
});
},
目标:编辑或新增商品错误时,跳出错误讯息(BS样式 吐司)通知
Toast分好几个放的优点:可以产生多次、独立的生命周期等...
npm i mitt
src > 新增methods资料夹 > 新增 emitter.js档案
emitter.js
// 汇入
import mitt from "mitt";
const emitter = mitt();
// 使用方式:emitter
export default emitter;
让内层都可以引用外层功能 provide
import emitter from '@/methods/emitter';
provide() {
return {
emitter,
};
},
index.js
path: "/dashboard",
name: "Dashboard",
component: () => import("../views/Dashboard.vue"),
// 多个,阵列
children: [
{
// http://localhost:8080/#/dashboard/products
path: "products",
component: () => import("../views/Products.vue"),
},
],
},
];
Products.vue
getProducts( page = 1 ) {
// 3.使用API抓值 取得商品列表
const api = `${process.env.VUE_APP_API}api/${process.env.VUE_APP_PATH}/admin/products/?page=${page}`;
this.$http.get(api).then((res) => {
if (res.data.success) {
console.log("res", res);
components > Pagination.vue
区域方法
src > methods > 新增filters.js
export function currency(num) {
const n = parseInt(num, 10);
return `${n.toFixed(0).replace(/./g, (c, i, a) => (i && c !== '.' && ((a.length - i) % 3 === 0) ? `, ${c}`.replace(/\s/g, '') : c))}`;
}
Products.vue
import { currency } from "../methods/filters.js";
methods: {
// 千分号 方法引用
currency,
Products.vue
<td class="text-right">{{ currency(item.origin_price) }}</td>
<td class="text-right">{{ currency(item.price) }}</td>
https://v3.cn.vuejs.org/api/application-config.html#globalproperties
// foo 自定义 通常 = function才好用 如 下例
app.config.globalProperties.foo = 'bar'
app.component('child-component', {
mounted() {
console.log(this.foo) // 'bar'
}
})
src > methods > 新增filters.js
export function currency(num) {
const n = parseInt(num, 10);
return `${n.toFixed(0).replace(/./g, (c, i, a) => (i && c !== '.' && ((a.length - i) % 3 === 0) ? `, ${c}`.replace(/\s/g, '') : c))}`;
}
main.js
import { currency } from "./methods/filters.js";
// 全域定义 方法
// https://v3.cn.vuejs.org/api/application-config.html#globalproperties
app.config.globalProperties.$filters = {
// 转千位数 及 时间方法
currency,
};
Products.vue
<td class="text-right">{{ $filters.currency(item.price) }}</td>
src/methods/pushMessageState.js 错误讯息吐司 判断式 给Coupons.vue & Orders.vue
Products.vue 内 吐司错误讯息程序码 会因为点击越来越多,故拆开 制作成另一方法
因此 methods > pushMessageState.js
并 全域汇入 给 Coupons.vue & Orders.vue 用
逻辑:因为直接用全域方法(main.js)只会有全域一个仓库(提升效能),但区域方法就会到处都有仓库
src/views/Orders.vue 订单页面
src/views/Coupons.vue 优惠页面
src/components/orderModal.vue
src/components/CouponModal.vue
src/components/Navbar.vue 连结样板
src/router/index.js 连结制作
src/main.js 全域汇入pushMessageState = $httpMessageState
mounted()
html标签跑完後执行
写在mounted就是防止抓不到dom元素 或抓不到html标签的问题产生
ex:Bootstrap Modal、吐司等等..
created()用在一开始要抓值时
ex: API资料
Products.vue(外)
import DelModal from "@/components/DelModal.vue";
// 口决:前内後外
<DelModal :item="tempProduct"/>
data() {
return {
tempProduct: {},
};
},
DelModal.vue(内)
props: {
item: {},
},
data() {
return {
modal: "",
};
Products.vue(外)
import DelModal from "@/components/DelModal.vue";
// 口决:前内後外
<DelModal @del-item="delProduct" />
delProduct() {
const url = `${process.env.VUE_APP_API}api/${process.env.VUE_APP_PATH}/admin/product/${this.tempProduct.id}`;
this.isLoading = true;
this.$http.delete(url).then((response) => {
this.isLoading = false;
console.log(response.data);
const delComponent = this.$refs.delModal;
delComponent.hideModal();
this.getProdcts();
});
},
DelModal.vue(内)
@click="$emit('del-item')"
emitter.js
// 汇入
import mitt from "mitt";
const emitter = mitt();
// 使用方式:emitter
export default emitter;
Dashboard.vue(父)
import emitter from "@/methods/emitter";
provide() {
return {
emitter,
};
},
ToastMessages.vue(子)
// 引用emitter (因 Dashboard.vue provide )
inject: ["emitter"],
父、子层区分
index.js
path: "/dashboard",
name: "Dashboard",
component: () => import("../views/Dashboard.vue"),
// 多个,阵列
children: [
{
// http://localhost:8080/#/dashboard/products
path: "products",
component: () => import("../views/Products.vue"),
},
],
},
];
let myApple = '123T456';
let arr = myApple.split("T");
consolog(arr) // [123,456]
let myApple2 = '1,2,3,4';
let arr2 = myApple.split(",");
consolog(arr) // [1,2,3,4]
<<: 如何建立 Windows USB 安装随身碟-适用 Win 10, Win Server 2019
>>: (World N0-1)! To Pass LookML-Developer Exam Guide
现在来学习CSS 如果说 HTML 是用来处理主要网页结构,CSS 就是来处理网页细节的。负责美化跟...
今天我们用实际的例子来练习各种 RxJS operators 的组合运用!在一般的应用程序里面,资料...
Pooling Layer 影像的spatial information不会因scale而消失,所以...
安装套件 Visual Studio Code 上有很多方便编写程序的扩充套件,能让我们在使用上更加...
风险 表示发生,可能会对价值或资产造成负面的冲击。 风险是外部威胁利用弱点对内部资产造成冲击的可能性...