PHP 會員留言板系統

延續前篇:PHP 會員登入系統

結構

  • MySQL留言資料庫(message)
  • 留言板頁面(msg.php)
  • 留言新增功能(msg-add.php)
  • 留言刪除功能(msg-del.php)

MySQL留言資料庫(message)

編碼:utf8_unicode_ci

名稱 型態 備註
id int AI
guest_id int
content text
date date

留言板頁面(msg.php)

  • 結合 message 的 guest_id 與 member 的 id
    在 message 的提取迴圈裡面,寫下指令 mysql_query( 讀取 member 資料表,WHERE條件設定 id=’$row[guest_id]’ )
<?php
mysql_connect("localhost", "root", "");
mysql_select_db("hahow");
mysql_query("SET NAMES UTF8");

//如果沒有登入的SESSION,就轉址
if (isset($_SESSION["email"])==FALSE) {
 header('Location: login.php');
}
?>
<!DOCTYPE html>
<html>
<head>
 <title>Message Board</title>
 <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
 <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<br><br><br><br>
<div class="container">
 <h3 class="text-center">會員留言板</h3>
 <hr>

<?php
$re=mysql_query("SELECT * FROM message");
//檢査有沒有留言數( message 資料表的資料筆數是否大於0):參考
if(mysql_num_rows($re)>0){
 //如果有留言,一筆一筆印出留言
 //使用 while 迴圈從 message 資料表一筆一筆讀取留言的方法:參考
 while($row=mysql_fetch_array($re))
 {
 //mysql_query() 括弧裡面的指令,使用變數時,要用單引號包起來,參考
 $memberRe=mysql_query("SELECT * FROM member WHERE id='$row[guest_id]'");
 $memberRow=mysql_fetch_array($memberRe);

 echo "<div class=\"panel panel-default\">
 <div class=\"panel-heading\">$memberRow[name] 
 <span class=\"pull-right\">$row[date]
 //以更改網址的方式強制讀取GET表單資料
 //正常的流程:使用者在GET表單輸入資料→送出轉址→新網址裡面有使用者輸入的資料
 //轉址到有特定 GET 表單 id 的 msg-del.php
 <a href=\"msg-del.php?id=$row[id]\" class=\"btn btn-danger btn-xs\">
 DELETE
 </a>
 </span>
 </div>
 <div class=\"panel-body\"> $row[content]
 </div>
 </div>";
 }
}

else{
 //沒有留言的話
 echo "<p class=\"text-center\">沒有任何訊息!</p>";
}
?>
 <hr>
 <p class="pull-right">以 <?php echo $_SESSION["name"]; ?> 的身份留言</p>
 <h4>新增留言</h4>
 <form action="msg-add.php" method="post">
 <textarea name="msg" class="form-control"></textarea>
 <br>
 <input type="submit" name="submit" value="送出" class="btn btn-primary btn-sm pull-right">
 </form>
</div>
</body>
</html>

留言新增功能(msg-add.php)

<?php
require("msg.php");

//設定日期格式
$date=date("Y-m-d");

$SaveNewMsg=mysql_query("INSERT INTO message(guest_id, content, date) 
//對應 msg.php 新增留言區的 POST表單
VALUES('$_SESSION[id]','$_POST[msg]','$date')");

//檢査
if(!$SaveNewMsg){
 echo "留言失敗";
}else{
 echo "留言成功";
}

//新增完畢轉回留言板
header('Location: msg.php');
?>

留言刪除功能(msg-del.php)

<?php
require("msg.php");

//抓取 msg.php 布好的GET表單的刪除ID
mysql_query("DELETE FROM message WHERE id='$_GET[id]'");

//刪除完畢轉回留言板
header('Location: msg.php');
?>

範例:會員登入網站

測試用帳號:123@123
測試用密碼:123

結語

  • 善用 Sublime + SFTP,存檔即上傳主機可以節省時間
  • bug 可能出現在各種地方,資料庫連線,上一份檔案的 session 沒有寫好,都可能有錯,必須小心

clock

REF: JavaScript30

先備知識

原理

  • transform: rotate:指針旋轉
  • transition-timing-function:指針走動效果
  • Date():指針依照現在時間旋轉到適當位置

範例

.clock
 .clock-face
  .hand.hour-hand
  .hand.min-hand
  .hand.second-hand
html
 background: #FFFFF0
 font-family: 'helvetica neue'
 text-align: center
 font-size: 10px

body
 font-size: 2rem
 display: flex
 flex: 1
 min-height: 100vh
 align-items: center

.clock
 width: 30rem
 height: 30rem
 border: 8px solid #086788
 border-radius: 50%
 margin: 50px auto
 position: relative
 padding: 2rem
 box-shadow: 3px 3px 5px #ccc

.clock-face
 position: relative
 width: 100%
 height: 100%
 transform: translateY(-3px)
 //account for the height of the clock hands

.hand
 width: 50%
 height: 6px
 background: #2EC4B6
 position: absolute
 top: 50%
 //指針以右邊為基點旋轉
 transform-origin: 100%
 //default: 50% 基點為中心
 transform: rotate(90deg)
 transition: all 0.05s
 transition-timing-function: cubic-bezier(0, 1.03, 0, 2.09)
const secondHand=document.querySelector(".second-hand");
const minHand=document.querySelector(".min-hand");
const hourHand=document.querySelector(".hour-hand");

function setDate(){
 
 const now=new Date();
 const seconds=now.getSeconds();
 const secondsDegrees=((seconds/60)*360)+90; //offset default 90 deg
 
 const mins=now.getMinutes();
 const minsDegrees=((mins/60)*360)+90;
 
 const hours=now.getHours();
 const hoursDegrees=((hours/12)*360)+90;
 
 secondHand.style.transform=`rotate(${secondsDegrees}deg)`;
 minHand.style.transform=`rotate(${minsDegrees}deg)`;
 hourHand.style.transform=`rotate(${hoursDegrees}deg)`;
 
 console.log(seconds);
 
 //59-60時指針會快速繞一圈(角度計算的關係),所以停止transition
 if(seconds==59){
 secondHand.style.transition="0s";
 }
 if(mins==59){
 minHand.style.transition="0s";
 }
 if(hours==11){
 hourHand.style.transition="0s";
 }
 
}

//run every second
setInterval(setDate, 1000);

Display

CODEPEN

JavaScript Date()

呼叫時間的方法

  1. 建立 Date 建構子物件
  2. 呼叫項目

範例

.text
function runTime(){
 
 //呼叫Date建構子物件
 //()甚麼都不加,表示抓取現在時間
 var now=new Date();

 //呼叫現在的秒&分&時
 var seconds=now.getSeconds();
 var mins=now.getMinutes();
 var hours=now.getHours();

 //呼叫現在的年份
 var years=now.getFullYears();
 
 document.querySelector(".text").textContent=`現在是${hours}時${mins}分${seconds}秒`;
 
}

//毎一秒偵測一次時間
setInterval(runTime, 1000);

Display

Piano Kit

REF: JavaScript30

音樂素材:魔王魂

説明

按下特定鍵盤按鈕,會出現鋼琴音與特效

先備知識

範例

.container
 .infos
 h1 Piano Keyboard
 .keys
 button.key(data-key="65")
 kbd A
 span C
 button.key(data-key="83")
 kbd S
 span D
 button.key(data-key="68")
 kbd D
 span E
 button.key(data-key="70")
 kbd F
 span F
 button.key(data-key="71")
 kbd G
 span G
 button.key(data-key="72")
 kbd H
 span A
 button.key(data-key="74")
 kbd J
 span B
 button.key(data-key="75")
 kbd K
 span C


audio(data-key="65" src="https://goo.gl/r6zBRo")
audio(data-key="83" src="https://goo.gl/ymMuIO")
audio(data-key="68" src="https://goo.gl/kbSq41")
audio(data-key="70" src="https://goo.gl/2SDjv7")
audio(data-key="71" src="https://goo.gl/JVhfKe")
audio(data-key="72" src="https://goo.gl/sRnzrM")
audio(data-key="74" src="https://goo.gl/N1WJBh")
audio(data-key="75" src="https://goo.gl/Epfm4A")
*
 //border: 1px solid #000
 font-family: Lato

body,html
 margin: 0
 padding: 0
 position: relative

.container
 width: 100%
 height: 100%
 text-align: center
 top: 35vh
 position: absolute
 
.infos
 margin-left: auto
 margin-right: auto
 max-width: 1200px
 width: 800px
 
 h1
 text-align: center

.keys
 display: inline-block
 .key
 width: 75px
 height: 75px
 background-color: #f2f2e9
 border: 2px solid #90a955
 transition: all 0.07s
 vertical-align: top
 kbd
 font-size: 26px
 font-weight: 700
 display: block
 span
 font-size: 16px

.playing
 border-color: #24260f
 transform: scale(1.1)
 box-shadow: 0 0 5px #90a955
function playSound(e){
 
 //抓到特定的audio
 //const定義常數:保持不變的値,相對於var 變數
 const audio=document.querySelector(`audio[data-key="${e.keyCode}"]`);
 //抓到特定的piano key
 const key=document.querySelector(`.key[data-key="${e.keyCode}"]`);
 if(!audio) return; //stop the function from running all together
 //如果沒有上面的if, 按不是piano的按鍵,會出現null
 audio.currentTime=0; //rewind to the start
 
 audio.play();
 key.classList.add("playing");
 
}

function removeTransition(e){
 
 if(e.propertyName!=="transform") return;//skip it if it is not transform
 this.classList.remove("playing");
 
}

const keys=Array.from(document.querySelectorAll(".key"));
//forEach 陣列裡面所有東西都...
keys.forEach(key => key.addEventListener('transitionend', removeTransition));

window.addEventListener("keydown", playSound);

/*****************
點擊播放功能新增區域
******************/
keys.forEach(key => key.addEventListener("click", clickPlay));
/*↑效果等於這個↓的迴圈
for(i=0;i<9;i++){
 keys[i].addEventListener("click", clickPlay);
}
*/
function clickPlay(){
 var dataKey=this.getAttribute("data-key");
 const audio2=document.querySelector(`audio[data-key="${dataKey}"]`);
 audio2.currentTime=0;
 audio2.play();
 
 this.classList.add("playing");
}

CODEPEN

箭頭函數

目的

簡化function的形式,只保留參數與函式

//ES5
function(參數){
  函式
}

//ES6
(參數) => {函式}

範例

var names=["Ian", "Moko", "Afro"];

//ES5
//names的毎筆資料會丟進function的參數
//變成全名
//陣列.map(),修改原陣列,變成新的陣列
var fullNames=names.map(
 function(name){
 return `${name} CHEN`;
 }
);
console.log(fullNames);//["Ian CHEN", "Moko CHEN", "Afro CHEN"]

//ES6
//(參數) => {函式}
var fullNames2=names.map(
 (name) => 
 {return `${name} CHEN`;});
console.log(fullNames2);//["Ian CHEN", "Moko CHEN", "Afro CHEN"]


//只有一個參數的話可以不用()
//也可以省略return,順便拿掉{}
var fullNames3=names.map(name => `${name} CHEN`);
console.log(fullNames3);//["Ian CHEN", "Moko CHEN", "Afro CHEN"]

進階:指定參數初始値

const numbers=[5,2,7,10,50];

//ES5
var total=0;
for(i=0;i<numbers.length;i++){
 total += numbers[i];
}

console.log(total) //74

//ES6
//reduce,陣列累加用法
const total2=numbers.reduce((total, number) => {
 return total += number;
},0)//←這邊的0等同於宣告total初始値=0,total從0開始累加

console.log(total2) //74

進階:多個述句(statement)

加分號結束後直接寫

button.btns click
button.btns click
button.btns click
#btnstatus
const btns=document.querySelectorAll(".btns");
btns.forEach((btn, n) => {
  btn.addEventListener("click", function(){
  btnstatus.textContent=`you clicked btn ${n+1}`;
 });
  btn.addEventListener("mousemove", function(){
  btnstatus.textContent=`you touched btn ${n+1}`;
 })
})

Display

模板字符串

説明

  • ES6新規範,允許在字串内置入變數﹐參數等
  • 變數﹐參數的地方用${}包起來
  • 整個字串要用反引號`包起來(不使用雙引號”包)

範例

.text
var a=3;
var b=2;

//整個字串要用反引號`包起來(不使用雙引號"包)
//變數﹐參數的地方用${}包起來
document.querySelector(".text").textContent=`計算變數a+b=${a+b}`;

Display

keyCode

JavaScript keyCode

JS用來識別特定鍵的鍵盤碼

鍵盤碼査詢

方法一:網頁査詢

Keycode

方法二:console.log

window.addEventListener("keydown",
 function(e){
 console.log(e.keyCode);
 }
);

//然後打開console,對著網頁區域任意按鍵盤,就會出現鍵盤碼

相同方法:event.which

window.addEventListener("keydown",
 function(e){
 console.log(e.which);
 }
);

//一樣打開console,對著網頁區域任意按鍵盤,就會出現鍵盤碼

Bootstrap格線系統

中文說明手冊

結構

Bootstrap的格線系統,把寬度切成12分,用橫向的row裝縱向的col,指定每個col佔12分長的多少分。

尺寸關鍵字

  • xs:超小螢幕 < 768px
  • sm:小螢幕 > 768px
  • md:中螢幕 > 992px
  • lg:大螢幕 > 1200px
link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css")

.row
 .col-lg-6.col-md-7.col-sm-4
 .col-lg-6.col-md-5.col-sm-8

//用row包覆column
//col可以向上繼承,無法向下繼承
//只指定lg → 不會套用到xs
//只指定xs → 可以套用到lg

顯示與隱藏

指定col在特定元素下顯示或隱藏

  • visible-尺寸:只在該尺寸顯示
  • hidden-尺寸:只在該尺寸隱藏
link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css")

.row
 .col-xs-12.visible-xs
  h3 XS(超小螢幕<768px)
 .col-xs-12.visible-sm
  h3 SM(小螢幕>768px)
 .col-xs-12.visible-md
  h3 MD(中螢幕>992px)
 .col-xs-12.visible-lg
  h3 LG(大螢幕>1200px)

偏移

col-尺寸-offset-欄位數:指定左側向右偏移的大小

link(rel="stylesheet", href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css")

.row
 .col-sm-8.col-sm-offset-4
  h3 偏移的區塊
  p 使用col-尺寸-offset-欄位數來指定左側偏移的大小

Display

CODEPEN