Flutter 安裝

Flutter 官網介紹 Windows 安裝方式:
https://flutter.dev/docs/get-started/install/windows
Flutter 官網介紹 Linux 安裝方式:
https://flutter.dev/docs/get-started/install/linux
所以需要安裝多種軟體

  • Flutter SDK
  • Android Studio
  • Xcode

到官網下載 Mac flutter SDK
Flutter SDK For MacOS 官方下載點:flutter_macos_2.2.1-stable.zip

1
2
3
4
5
6
7
8
# 在桌面建立一個 development 資料夾
mkdir ~/Desktop/development

# 進入 development 目錄
cd ~/Desktop/development

# 解壓縮 Flutter.zip 壓縮檔到此目錄
unzip ~/Downloads/flutter_macos_2.2.1-stable.zip

Set3 : 設定Flutter指令路徑

1
2
3
4
export PATH=$PATH:[Flutter目錄路徑]/flutter/bin
//執行
export PATH=$PATH:/Users/larahuang/Desktop/development/flutter/bin

永久開啟

1
2
3
4
5
6
7
8
9
10
# 使用 nano 文字編輯器,開啟 「.bash_profile」
nano .bash_profile

# 在「.bash_profile」檔案中加入 Flutter 指令路徑:
export PATH=$PATH:/Users/CalvinHuo/Desktop/development/flutter/bin

# ctrl + x 存檔離開,將永久可使用 flutter 指令,不會因會關閉終端機,下次進來就無法使用 flutter 指令

#新增的設定檔,需要重開終端機才會生效,若不想重開,可以執行以下指令
source ~/.bash_profile

檢查安裝(必須在設定Flutter指令路徑下)

1
2
3
4
5
6
flutter doctor

//查看位元
uname -a
RELEASE_X86_64 x86_64

下載android studio

android studio安裝說明

1
flutter doctor android-licenses

參考資料

純javascript 輪播

  1. 綁定每個輪播的區域:
  2. 設置輪播圖容器:
  3. 定義輪播的區域有幾個與索引從第0個開始:
  4. 設置一定時間重複setinterval函式:獲取當前索引,獲取下一個輪播圖的索引,取於防止超出範圍,讓當前輪播圖項左移出屏幕,讓下一個輪播圖向左進入屏幕;style.transition與style.transform=>translateX 的使用
  5. 移動結束後,設置下一張輪播圖位置,準備下一次移動setTimeout 函式的使用
1
2
3
4
5
6
7
8
//html
<div class="carousel">
<div class="carousel_inner">
<div class="carousel_item" style="background-color: aqua;"></div>
<div class="carousel_item"
style="background-color: yellow;"></div>
</div>
</div>

Css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
body{
margin:0;
padding:0;
}

.carousel_inner{
width: 100vw;
overflow: hidden;
}

.carousel_item{
width: 100vw;
height: 100vw;
}

JavaScript.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 綁定carouselItem
let carouselItem =document.getElementsByClassName("carousel_item");
// 取得寬度
let width = carouselItem[0].clientWidth;
// 算出carouselItem有幾個
let lenth = carouselItem.length;
// 容器
let carouselInner = document.getElementsByClassName("carousel-inner");
carouselInner[0].style.width =`${ width * length }px`;

let i = 0;

// 每個輪播元素一定時間重複
setInterval(() =>
// 獲取當前索引
let current = i % lenth;
console.log('獲取當前索引',current);
let next = (i + 1) % lenth;
console.log('獲取下一個輪播圖的索引',next);
// 塞入當前的 translateX位置
carouselItem[current].style.transform = `translateX(${0 - (current + 1) * width}px)`;
// 塞入下一頁的 translateX位置
carouselItem[next].style.transform = `translateX(${0 - ((current + 1) % lenth) * width}px)`;
i++;
// 移動結束後,設置下一張輪播圖位置,準備下一次移動
setTimeout(() => {
carouselItem[current].style.transition = "";
carouselItem[next].style.transition = "";

// 輪播每個元素
for(let j = 0; j < lenth; j++){
// 當前元素不變
if(j == i % lenth) continue;
if(j < i % lenth) {
// 当前元素左邊的元素按順序移動放置到列表的后面做進行準備
carouselItem[j].style.transform = `translateX(${(lenth - j - i % lenth) * width}px)`;
}
else{
// 当前元素右邊的元素移動到當前元素的後面,防止輪播圖切換时中間有留白
carouselItem[j].style.transform = `translateX(${0 - ((current + 1) % lenth) * width}px)`;
}
}
i %= lenth;
},800)
},2000)

js 輪播 加左右鍵與引導按鈕

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//html
<div class="banner_container">
<ul class="img_box">
<li><img src="https://cdn.pixabay.com/photo/2024/04/13/11/29/muffins-8693748_1280.jpg" alt=""></li>
<li><img src="https://cdn.pixabay.com/photo/2023/07/04/17/13/mallow-8106680_1280.jpg" alt=""></li>
<li><img src="https://cdn.pixabay.com/photo/2024/01/04/09/01/bird-8486864_1280.jpg" alt=""></li>
<li><img src="https://cdn.pixabay.com/photo/2024/05/15/07/59/flowers-8763039_1280.jpg" alt=""></li>
<li><img src="https://cdn.pixabay.com/photo/2020/06/24/19/50/garden-5337535_1280.jpg" alt=""></li>
<li><img src="https://cdn.pixabay.com/photo/2024/05/26/10/15/bird-8788491_1280.jpg" alt=""></li>
</ul>
<!--引導-->
<ul class="sel_box">
<li data-index="0"></li>
<li data-index="1"></li>
<li data-index="2"></li>
<li data-index="3"></li>
</ul>
<!--左右鍵-->
<div class="left_btn"><</div>
<div class="right_btn">></div>
</div>

Css

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
* {
padding: 0;
margin: 0;
box-sizing: border-box;
}

.banner_container {
position: relative;
margin: 100px;
width: 400px;
height: 250px;
overflow: hidden;
}

ul.img_box {
position: absolute;
left: 0;
top: 0;
transform: translateX(-400px);
}

.img_box li {
float: left;
list-style: none;
}


.img_box li img {
width: 400px;
}


.sel_box {
position: absolute;
bottom: 15px;
left: 50%;
transform: translateX(-50%);
}

.sel_box li {
list-style: none;

display: inline-block;
width: 10px;
height: 10px;
background-color: pink;
margin-right: 3px;
border-radius: 5px;
transition: all 0.4s;
}


.left_btn {
position: absolute;
top: 50%;
left: 0;
transform: translateY(-50%);
width: 25px;
height: 30px;
background-color: #fff;
line-height: 30px;
padding-left: 3px;

cursor: pointer;
}

.right_btn {
position: absolute;
top: 50%;
left: 375px;
transform: translateY(-50%);
width: 25px;
height: 30px;
background-color: #fff;
line-height: 30px;
padding-left: 5px;
cursor: pointer;
}



.sel_box .cur {
background-color: red!important;
transform: scale(1.3);
}

JavaScript

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//綁定圖片Box
let img_box = document.querySelector('.img_box');
let imgs = document.querySelectorAll('img');

//綁定引導Box
let sel_box = document.querySelector('.sel_box')
let lis = sel_box.querySelectorAll('li');

//左右按鈕
let left_btn = document.querySelector('.left_btn');
let right_btn = document.querySelector('.right_btn');

//索引第幾張
let index = 0;
let timer = null;

//設置容器大小
let imgContainerW = img_box.offsetWidth
img_box.style.width = imgContainerW * imgs.length + 'px'
//設置容器位置
img_box.style.left = 0 + 'px';

//設置第一個小圖為當前按鈕
lis[0].className = 'cur'

//輪播切換
const swapImg =() =>{

img_box.style.left = -index * imgContainerW + 'px';

for (let i = 0; i < lis.length; i++) {
lis[i].className = '';
}

lis[index].className = 'cur'
}

const swapFormat =() =>{
index++;

if (index >= 4) {

index = 4;
img_box.style.transition = 'all, linear, 1s';
img_box.style.left = -index * imgContainerW + 'px';
for (let i = 0; i < lis.length; i++) {
lis[i].className = '';
}

lis[0].className = 'cur'

//通過定時器立馬切換到第一張
setTimeout(()=>{
index = 0;
img_box.style.transition = '';
swapImg();
}, 1500)


} else {
img_box.style.transition = 'all, linear, 1.5s';
swapImg();
}
}
//
timer = setInterval(swapFormat, 3000)

//右按鈕 =>自動播放
right_btn.addEventListener('click', ()=>{
swapFormat();
})

//左按鈕
left_btn.addEventListener('click', () =>{
index--;

if (index < 0) {
index = -1
img_box.style.transition = 'all, linear, 1.5s';
img_box.style.left = -index * imgContainerW + 'px';
for (let i = 0; i < lis.length; i++) {
lis[i].className = '';
}

lis[3].className = 'cur'


setTimeout(()=>{
index = 3
img_box.style.transition = '';
swapImg();
}, 1000)

} else {
img_box.style.transition = 'all, linear, 1.5s';
swapImg();
}
})


img_box.addEventListener('mouseover', () =>{
clearInterval(timer)
})

right_btn.addEventListener('mouseover', () =>{
clearInterval(timer)
})

left_btn.addEventListener('mouseover', () =>{
clearInterval(timer)
})


img_box.addEventListener('mouseout', ()=> {
timer = setInterval(swapFormat, 3000)
})

left_btn.addEventListener('mouseout', ()=> {
timer = setInterval(swapFormat, 3000)
})

right_btn.addEventListener('mouseout', () =>{
timer = setInterval(swapFormat, 3000)
})

javascript 計算總和的幾種方法

for迴圈加總

1
2
3
4
5
6
7
8
9
const SumFor=(arr)=>{
var sum=0;
for (var i = 0; i < arr.length; i++) {
sum += arr[i];
};
return sum;
}
var data= [1, 1, 1];
console.log(SumFor(data));

forEach遍歷 加總

1
2
3
4
5
6
7
8
9
const sumforEach = (arr) =>{
var sum=0;
arr.forEach((el)=>{
sum+=el;
});
return sum;
}
var data= [1, 1, 1];
console.log(sumforEach(data));

reduce() 方法將一個累加器及陣列中每項元素(由左至右)傳入回呼函式,將陣列化為單一值。

1
2
3
4
var sum = [1, 1, 1].reduce( (a, b)=> {
return a + b;
}, 0);
console.log(sum);

1
2
3
4
5
6
7
8
9
10
11
12
const array =[
{name:'apple',price:25,qty:1},
{name:'orange',price:10,qty:1},
{name:'piniaApple',price:45,qty:1},
]

//將map價格取出=>reduce累加=> 簡化以後
const SumReduce = (arr)=>{
return arr.map(el=>el.price).reduce((a,b)=>a+b);
}
console.log(SumReduce(array));

javascript 上傳表單 (input type=file)

javascript 上傳表單 ;FileReader 表單上傳,檔案資料表示為 base64(單個)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
const previewFile = () =>{
const preview = document.querySelector("img");
const mask = document.querySelector(".mask");
const file = document.querySelector("input[type=file]").files[0];
const reader = new FileReader();
//
reader.addEventListener(
"load",
() => {
// 將圖片檔案轉換為 Base64 字串
preview.src = reader.result;
preview.classList.add("active");
mask.classList.add("maskActive");
},
false,
);

if (file) {
console.log(file)
reader.readAsDataURL(file);
}
}

表單上傳多個文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
const previewFiles = () => {
const preview = document.querySelector("#preview");
const files = document.querySelector("input[type=file]").files;

const readAndPreview = (file) => {
//確保“file.name”符合我們的擴充標準
if (/\.(jpe?g|png|gif)$/i.test(file.name) ) {
const reader = new FileReader();

reader.addEventListener("load",() => {
//Image()
const image = new Image();
image.height = 100;
image.title = file.name;
image.src = reader.result;
preview.appendChild(image);
},false);

reader.readAsDataURL(file);
}
}

if (files) {
Array.prototype.forEach.call(files, readAndPreview);
}
}

const picker = document.querySelector("#browse");
picker.addEventListener("change", previewFiles);

參考資料

css Hover箭頭轉向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
<div class="one" >
<span><a href="#"></a></span>
</div>

<div class="two" >
<a href="#"></a>
</div>

<style lang="scss">
body{
padding:0px;
margin:50px;
}
.one{
margin:0;
padding:0;
span{
height:50px;
text-align:center;
a{
border:3px solid red;
width:35px;/*箭頭大小*/
height:35px;/*箭頭大小*/
border-width:3px 0 0 3px;
display:inline-block;
-moz-transform-origin:center center;
-moz-transform:rotate(135deg);
-moz-transition:all .3s ease-in .1s;
-webkit-transform-origin:center center;
-webkit-transform:rotate(135deg);
-webkit-transition:all .3s ease-in .1s;
-o-transform-origin:center center;
-o-transform:rotate(135deg);
-o-transition:all .3s ease-in .1s;
-ms-transform-origin:center center;
-ms-transform:rotate(135deg);
-ms-transition:all .3s ease-in .1s;
transform-origin:center center;
transform:rotate(135deg);
transition:all .3s ease-in .1s;
&:hover{
-moz-transform:rotate(225deg);
-moz-transition:all .3s ease-in .1s;
-webkit-transform:rotate(225deg);
-webkit-transition:all .3s ease-in .1s;
-o-transform:rotate(225deg);
-o-transition:all .3s ease-in .1s;
-ms-transform:rotate(225deg);
-ms-transition:all .3s ease-in .1s;
transform:rotate(225deg);
transition:all .3s ease-in .1s;
}/*a:hover*/
}/*a*/
}/*span*/
}/*one*/

.two{
margin-top:50px;
a {
width: 0;
height: 0;
border-right: 30px solid transparent;
border-bottom: 30px solid blue;
border-left: 30px solid transparent;
display:inline-block;
-moz-transform-origin:center center;
-moz-transform:rotate(90deg);
-moz-transition:all .3s ease-in .1s;
-webkit-transform-origin:center center;
-webkit-transform:rotate(90deg);
-webkit-transition:all .3s ease-in .1s;
-o-transform-origin:center center;
-o-transform:rotate(90deg);
-o-transition:all .3s ease-in .1s;
-ms-transform-origin:center center;
-ms-transform:rotate(90deg);
-ms-transition:all .3s ease-in .1s;
transform-origin:center center;
transform:rotate(90deg);
transition:all .3s ease-in .1s;
&:hover {
-moz-transform:rotate(180deg);
-moz-transition:all .3s ease-in .1s;
-webkit-transform:rotate(180deg);
-webkit-transition:all .3s ease-in .1s;
-o-transform:rotate(180deg);
-o-transition:all .3s ease-in .1s;
-ms-transform:rotate(180deg);
-ms-transition:all .3s ease-in .1s;
transform:rotate(180deg);
transition:all .3s ease-in .1s;
}
}
}

</style>

js 點擊新增時間

js 點擊重新新增時間

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
<button class="addBtn">Add</button>
<h1 class="countdownTitle">點擊新增分鐘</h1>
<!-- 顯示預計回來的時間 -->
<p class="newTimeStemp"></p>

<script>
//綁定標題
const countdownClockTitle = document.querySelector(".countdownTitle");
//顯示預計回來的時間
const newTimeStemp = document.querySelector(".newTimeStemp");
//綁定點擊按鈕
const addBtn = document.querySelector(".addBtn");
//定義countdown
let countdown;
// 時間的換算
const showTimer = (inputSeconds) => {
const minutes = Math.floor(inputSeconds / 60);//除以60取整數
const remainderSeconds = inputSeconds % 60;//除以60取余數
//塞進去畫面
countdownClockTitle.textContent = `
${minutes}:${remainderSeconds < 10
? "0" + remainderSeconds
: remainderSeconds}
`;
}
//顯示時間
const showTimeStemp = (done) => {
const newTime = new Date(done);
const hours = newTime.getHours();
const minutes = newTime.getMinutes();
newTimeStemp.textContent = `
點擊時間 ${hours}:${minutes}
`
}
const timer =(inputSeconds) => {
console.log('timer()',inputSeconds)
const now = Date.now();
const done = now + inputSeconds * 1000;
showTimer(inputSeconds);
showTimeStemp(done);
// 倒數計時
countdown = setInterval(()=>{
const tiemleft = Math.round((done-Date.now())/ 1000);
if(tiemleft<= 0){
clearInterval(countdown);
return
}
showTimer(tiemleft)
}, 1000)
}

const handleAddBtn = ()=> {
// 清除倒數計時
clearInterval(countdown); // clear countdown
// 綁定表單
const timerInput = Number(1800);
// timerInput.value=1800;
if(timerInput > 0){
// console.log(timerInput.value)
timer(timerInput)
}

}
addBtn.addEventListener("click", handleAddBtn)
</script>
### js setInterval 的應用

js 表單

表單選擇與表單全選checkbox checked && checkbox All checked

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
js
// 選單選擇數據
const radioLists=[
'Html','Css','JavaScript','Node','Vue'
]
// 渲染到選擇表單
const radioGroup = document.querySelector('.radio_group');
let arr = "";
radioLists.forEach((item) => {
//加法赋值运算符
arr += `<label class="radioBox" for="${item}" name="fav_language" >${item}<input type="checkbox" value="${item}" id="${item}"><span class="square-mark"></span><label>`;
})
radioGroup.innerHTML = arr;

// 綁定可選擇表單
const checkboxLists =document.querySelectorAll('.radioBox input');
// 綁定全選表單Class selectAll
const selectAll= document.querySelector('.selectAll');

//監控點擊全選表單
selectAll.addEventListener('change', function () {
// 綁定所有複選表單
checkboxLists.forEach(function (item,index) {
item.checked = this.checked;
}, this)
// 原來的選擇數據
const originalData =[
'Html','Css','JavaScript','Node','Vue'
]

//***提示 =>提示 全選變不選時 數據要清空
selectAll.checked ? selectObject =originalData : selectObject=[];
innerHtmlUl(selectObject)
})

//這裡選擇的數據必須全局
var selectObject =[];
//選擇項目或是不選項目時的數據必須通過這個函式來執行
function innerHtmlUl (selectObject){
const ulContent = document.querySelector('ul');
let arry = "";
selectObject.forEach((x) => {
//加法赋值运算符
arry += `<li >${x}<li>`;
})
ulContent.innerHTML = arry ;
}

// 遍歷 checkboxLists
for (let i = 0; i < checkboxLists.length; i++) {
//監控單個 checkboxLists[i]
checkboxLists[i].addEventListener( "change",function (){
if(checkboxLists[i].checked){
selectObject.push(checkboxLists[i].value);
if(selectObject.length>=checkboxLists.length) {
selectObject =radioLists
}
innerHtmlUl(selectObject)
}else{
selectObject.splice(checkboxLists[i].value,1);
innerHtmlUl(selectObject)
}
selectObject.length==checkboxLists.length ? selectAll.checked=true:selectAll.checked=false;
})
}

Vite Vue 麵包屑拖曳(語法糖)

Vite Vue 麵包屑拖曳(語法糖)

  • 點擊左邊菜單項目新增到麵包屑:陣列新增項目 => 使用 push()
  • 過濾掉相同的菜單項目並顯示在麵包屑:
    1.定義一個空陣列
    2.點擊菜單按鈕=>如果if從localStorage.getItem回來的數據不為null=> 原來的陣列等於localStorage.get回來的數據 ;無論localStorage.getItem回來的數據是否是null;都必須新增項目並過濾Fotmat (forEach遍歷 如果 空陣列與原陣列沒有發現相同那就新增push項目並且將數據localStorage.setItem)
  • 刪除麵包屑項目:獲取JSON.parse(localStorage.getItem('horizontalFilter')as string) =>如果 length 大於 0 =>刪除splice(id,1) 並且在setItem到localStorage 並且回調 在渲染的陣列get回來畫面上
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
<template>
<div class="w-fill admin">
<div class="side left">
<div class="tabs" v-for="(item) in sideLists" :key="item.id">
<div class="tab">
<input type="radio" :id="item.id" name="rd">
<label class="tab-label hvr-underline-from-right" :for="item.id">
<i :class="item.icon"></i>
{{ item.title }}
</label>

<div class="tab-content">
<ul v-if="item.children">
<li class="hvr-underline-from-right" v-for="(i,id) in item.children"
:key="id"
:class="active === i.id ? 'isActive' : ''"
@click="sendClickActive(i)">
<router-link :to="i.href"> {{ i.title }}</router-link>
</li>
</ul>
</div>
</li>
</div>
</div>
</div><!---/side-->
<div class="rightContent">
<!--麵包屑--->
<div class="horizontal_menu">
<!--打開就看到首頁按鈕-->
<ul class="horizontalNav home">
<li v-for="item in homeItem" :key="item.title">
<router-link :to="item.href">{{ item.title }} </router-link>
</li>
</ul>

<ul class="horizontalNav">
<li v-for="item in horizontalfilter" :key="item.title">
<router-link :to="item.href">{{ item.title }} </router-link>
</li>
</ul>
</div>

<router-view />
</div><!---/rightContent-->
</div>
</template>

<script setup lang="ts">
import { ref, computed ,onMounted} from "vue"
import { useRouter } from 'vue-router'

interface horizontalNavType{
id:string,
title:string |any ,
href:string | any,
}
// 麵包屑陣列
const horizontal=ref<horizontalNavType[]>([]);
// 過濾的麵包屑陣列
const horizontalfilter=ref<horizontalNavType[]>([]);
// ***過濾掉重複函式
const horizontalfilterFormat =(horizontal:any) =>{
let filteredItems = horizontal;
const getResult: any[] = [];
filteredItems.forEach((item:any) => {
if (!getResult.find((r: { title: string; })=> r.title === item.title)) {
getResult.push(item);
localStorage.setItem('horizontalFilter',JSON.stringify( getResult)) ;
}
})
}
// 點擊左邊菜單新增到使用的麵包屑
const clickActiveSide = (i: horizontalNavType) => {
activeSide.value = i.id;
try{
let query: any={
id:i.id,
title:i.title,
href:i.href
}
if(JSON.parse(localStorage.getItem('horizontalFilter')as string)!=null){
let get =JSON.parse(localStorage.getItem('horizontalFilter')as string);
horizontal.value=get;
}
horizontal.value.push(query);
horizontalfilterFormat(horizontal.value)
horizontalfilter.value= JSON.parse(localStorage.getItem('horizontalFilter')as string);
}
catch(err){
console.log(err)
}
}
// 刪除麵包屑項目
const deleteHorizontalNav =(title:any |string)=>{
const lists =JSON.parse(localStorage.getItem('horizontalFilter')as string);
if(lists.length>0 ){
lists.splice(title,1);
localStorage.setItem('horizontalFilter', JSON.stringify(lists)) ; horizontalfilter.value= JSON.parse(localStorage.getItem('horizontalFilter')as string);
}else{
router.push({ path: '/admin'})
}
}
const activeSide = ref<string>('')
const isActiveSide = ref<boolean>(false);
interface sideListType{
id:string,
title:string,
icon:string,
children:object,
}
//左手邊菜單
const sideLists=ref<sideListType[]>([
{
id:'buildSet',
title: "建檔設定",
icon:"fa-solid fa-address-book",
children: [
{id:'buildSet1-1',title: "工程資料建檔",href:"/one"}
{id:'buildSet1-2',title: "供料資料建檔",href:"/two"}
{id:'buildSet1-3',title: "供料資料建檔",href:"/three"}
{id:'buildSet1-4',title: "供料資料建檔",href:"/four"}
]
},
{
id:'dataSearch',
title: "資料搜尋",
icon:"fa-solid fa-address-book",
children: [
{id:'dataSearch1-1',title: "業主資料查詢",href:"/one"}
{id:'dataSearch1-2',title: "工程資料查詢",href:"/two"}
{id:'dataSearch1-3',title: "採購合約查詢",href:"/three"}

]
}
])
//首頁麵包屑定義
const homeItem =ref<any[]>([]);
// 打開時 讓選單可以出現首頁
const homeBtn=()=>{
const query={
id:'home',
title:'首頁',
href:'/admin',
}
homeItem.value.push(query);
}
onMounted(() => {
homeBtn();
localStorage.removeItem('horizontalFilter')
});
</script>



<style lang="scss" >
//左邊菜單
.admin {
display: flex;
width:100%;
//Side
.side{
border-right: 2px solid #c6e2ff;
background-color: #c6e2ff;
flex-direction: column;
max-width: 240px;
min-height: 100vh;
overflow-y: calc(100vh - 0px);
padding-top: 25px;
width: 240px;
ul {
list-style-type:none;
margin-block-start: 0em;
margin-block-end: 0em;
padding-inline-start: 35px;
}
.tabs {
overflow: hidden;
.tab{
color: #333;
overflow: hidden;
width: 100%;
input {
display: none;
&:checked{
~ .tab-content {
max-height: 100vh;
padding: .2em 0px .2em 1em;
}
}
}
.tab-label {
cursor: pointer;
display: flex;
justify-content: space-around;
padding: 0.6em;
font-size: 16px;
i {
color: #3F51B5;
font-size: 20px;
}
&:after {
content: "❯";
width: 1em;
height: 1em;
text-align: center;
transition: all 0.35s;
}
}
.tab-content{
max-height: 0;
padding: 0 1em;
color: #2c3e50;
transition: all 0.35s;
}
}
}
}

}


.rightContent{
display: flex;
flex-direction: column;
width: calc(100% - 240px);
max-width: 100%;

.horizontal_menu{
display: flex;
.horizontalNav{
display: flex;
list-style-type: none;
margin-block-start: 0em;
margin-block-end: 0em;
padding: 5px 0px;
li{
display: flex;
list-style-type: none;
margin: 0% 4px;
a{
text-decoration: none;
background-color: #409eff;
color: white;
padding: 8px 18px;
border-radius: 8px 8px;
-webkit-border-radius: 8px 8px;
i{
color: white;
&:hover{
color#ddd;
}
}
}
}
}
}
}
<style>

特別講解過濾重複陣列

  • forEach() js 遍歷
  • find() js 傳回通過測試的陣列中第一個元素的值)
  • push js 新增
  • localStorage.setItem為陣列時必須使用JSON.stringify()
  • localStorage.getItem為陣列時必須使用JSON.parse(localStorage.getItem('horizontalFilter')as string)
  • 1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const horizontalfilterFormat =(horizontal:any) =>{
    let filteredItems = horizontal;
    const getResult: any[] = [];
    filteredItems.forEach((item:any) => {
    if (!getResult.find((r: { title: string; })=> r.title === item.title)) {
    getResult.push(item);
    localStorage.setItem('horizontalFilter',JSON.stringify( getResult)) ;
    }
    })
    }

    gitHib

    增加可以拖動功能

    安裝swiperjs

    1
    2
    3
    ```
    npm install swiper --save

    檔案node_modules/swiper/swiper-bundle.css另存到@/assets/swiper-bundle.css 並且到main.ts全局加入

    1
    import '@/assets/css/swiper-bundle.css';
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    <template>
    <div class="horizontal_menu">
    <ul class="horizontalNav">
    <li v-for="item in homeItem" :key="item.title">
    <router-link :to="item.href">{{ item.title }} </router-link>
    </li>
    </ul>
    <swiper
    :modules="modules"
    :slides-per-view="3"
    :space-between="10"
    :loop="false"
    :autoplay="false"
    @swiper="onSwiper"
    @slideChange="onSlideChange"
    >
    <swiper-slide v-for="item in horizontalfilter" :key="item.title">
    <a :href="item.href">{{ item.title }}
    <i class="icon-cross"
    @click.prevent="deleteHorizontalNav(item.title)">
    </i>
    </a>
    </swiper-slide>
    </swiper>
    </div>
    </template>


    <script setup lang="ts">
    import { ref, computed ,onMounted} from "vue"
    import { useRouter } from 'vue-router'
    import { homeMenuType } from "@/types/navMenuType"
    import { Swiper, SwiperSlide } from 'swiper/vue'//引入
    import { Autoplay, Pagination, Navigation, Scrollbar }from 'swiper/modules'//解構取出

    interface horizontalNavType{
    id:string,
    title:string |any ,
    href:string | any,
    }
    const horizontal=ref<horizontalNavType[]>([]);
    const horizontalfilter=ref<horizontalNavType[]>([]);
    //過濾掉重複
    const horizontalfilterFormat =(horizontal:any) =>{
    let filteredItems = horizontal;
    const getResult: any[] = [];
    filteredItems.forEach((item:any) => {
    if (!getResult.find((r: { title: string; })=> r.title === item.title)) {
    getResult.push(item);
    localStorage.setItem('horizontalFilter',JSON.stringify( getResult)) ;
    }
    })
    }
    // Swiper定義
    const modules = [Autoplay, Pagination, Navigation, Scrollbar]
    const onSwiper = (swiper:any) => {
    console.log(swiper);
    };
    const onSlideChange = () => {
    console.log('slide change');
    };

    // 點擊左邊菜單新增到使用頁眉
    const clickActiveSide = (i: horizontalNavType) => {
    activeSide.value = i.id;
    try{
    let query: any={
    id:i.id,
    title:i.title,
    href:i.href
    }
    if(JSON.parse(localStorage.getItem('horizontalFilter')as string)!=null){
    let get =JSON.parse(localStorage.getItem('horizontalFilter')as string);
    horizontal.value=get;
    }
    horizontal.value.push(query);
    horizontalfilterFormat(horizontal.value)
    horizontalfilter.value= JSON.parse(localStorage.getItem('horizontalFilter')as string);
    }
    catch(err){
    console.log(err)
    }
    }

    const deleteHorizontalNav =(title:any |string)=>{
    const lists =JSON.parse(localStorage.getItem('horizontalFilter')as string);
    if(lists.length>0 ){
    lists.splice(title,1);
    localStorage.setItem('horizontalFilter', JSON.stringify(lists)) ; horizontalfilter.value= JSON.parse(localStorage.getItem('horizontalFilter')as string);
    }else{
    router.push({ path: '/admin'})
    }
    }
    const homeItem =ref<any[]>([])
    // 打開時 讓選單可以出現首頁
    const homeBtn=()=>{
    const query={
    id:'home',
    title:'首頁',
    href:'/admin',
    }
    homeItem.value.push(query);
    }
    onMounted(() => {
    homeBtn();
    localStorage.removeItem('horizontalFilter')
    });
    </script>



    <style lang="scss">
    .horizontal_menu{
    display: flex;
    justify-content: flex-start;
    .horizontalNav{
    display: flex;
    padding-inline-start: 0px;
    width: 55px;
    li{
    margin: auto 8px;
    }
    a{
    width: 80px;
    display: block;
    text-align: center;
    line-height: 38px;
    background-color: cornflowerblue;
    color:white;
    padding: 8px 14px;
    height: 38px;
    border-radius: 8px;
    }
    }
    .swiper{
    margin-left: 15px;
    overflow: visible;
    display: flex;
    align-items: center;
    &.swiper-horizontal{
    text-align: left;
    margin-left: 45px;
    }
    .swiper-wrapper{
    display: flex;
    justify-content: flex-start;
    overflow: visible;
    align-items: center;
    .swiper-slide{
    width: auto!important;background-color: cornflowerblue;
    color: white;
    overflow: visible;
    display: flex;
    align-items: center;
    padding-left: 8px;
    padding-right: 8px;
    border-radius: 7px;
    height: 38px;
    a{
    color: white;
    display: flex;
    align-items: center;
    .icon-cross{
    margin-left: 5px;
    }
    }
    }
    }
    }
    </style>

    gitHub 麵包屑拖曳

    js相同陣列過濾去除

    css手風琴菜單

    Vite 上傳照片 使用fileReader

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    <template>
    <el-form
    ref="ruleFormRef"
    :model="ruleForm"
    :rules="rules"
    class="add_person_set_form">
    <div class="col_4">
    <el-form-item class="avatar_input" prop="photo1">
    <div class="pic_img" v-show="file!=''">
    <img :src="file" />
    </div>
    <div class="file_discription">
    {{fileContent}}
    </div>
    <label for="imgFile" class="file-upload">
    <i class="fa fa-cloud-upload"></i>
    上傳照片
    <input
    class="form-control"
    type="file" id="imgFile" accept=".jpg,.jpeg,.png"
    @change="fileChangehandler" />
    </label>
    </el-form-item>
    </div>
    </el-form>
    </template>

    <script setup lang="ts">
    import { ref,reactive, onMounted,computed } from 'vue'
    import type { FormInstance, FormRules } from 'element-plus'
    const ruleFormRef = ref<FormInstance>()
    // 表單
    const ruleForm=ref<any>({
    photo1: "",//照片})

    //檔案路徑
    const file =ref<string>('');
    //檔案名稱
    const fileContent=ref<string>('');
    const fileChangehandler =(e:any)=>{
    console.log(e.target)
    //建立 fileReader 物件
    const reader = new FileReader
    //readAsDataURL 方法: 開始讀取指定的 Blob,讀取完成後屬性 result 將以 data: URL 格式(base64 編碼)的字串來表示讀入的資料內容。
    reader.readAsDataURL(e.target.files[0])
    //onload 事件: 於讀取完成時觸發。
    reader.onload = () => {
    //reader.result 為檔案的內容
    file.value = reader.result;
    console.log('file.value:', file.value)
    fileContent.value=e.target.files[0].name;
    }
    reader.onerror = (error) => {
    console.log('Error:', error)
    }
    }

    =>建立 fileReader 物件
    =>readAsDataURL 方法: 開始讀取指定的 Blob,讀取完成後屬性 result 將以 data: URL 格式(base64 編碼)的字串來表示讀入的資料內容。
    =>onload 事件: 於讀取完成時觸發。
    =>onerror 錯誤訊息
    參考資料