網頁

網頁

客製 Bootstrap 5.3

@import '../node_modules/bootstrap/scss/functions';

// 要修改的變數放這裡
$primary: #a8c430;

@import '../node_modules/bootstrap/scss/variables';
@import '../node_modules/bootstrap/scss/variables-dark';

// 要新增的 map 放這裡
$sam-colors: (
  'sam1': #900,
  'sam2': #f10,
);

$theme-colors: map-merge($theme-colors, $sam-colors);

@import '../node_modules/bootstrap/scss/bootstrap';

.custom-class {
  display: none;
}
@include media-breakpoint-up(md) { // 斷點設定
  .custom-class {
    display: block;
  }
}
網頁

Sequelize 筆記

// db.js

const { Sequelize } = require('sequelize');

const sequelize = new Sequelize('database', 'root', '1234', {
  dialect: 'mysql',
  timezone: '+08:00',
  define: {
    underscored: true, // 欄位有下㡳線
    freezeTableName: true, // 不要自動改變 table 名稱
  },
});

module.exports = sequelize;

// app.js

sequelize
  .sync()
  .then(() => {
    app.listen(3000, () => {
      console.log('listening on port 3000');
    });
  })
  .catch((err) => {
    console.log(err);
  });

// model

const TeamTaiwan = sequelize.define(
  'teamTaiwan',
  { ... },
  { tableName: 'team_taiwan' }
);

module.exports = TeamTaiwan;

---

const TaiwanPlayer = sequelize.define(
  'taiwanPlayer',
  { ... },
  { tableName: 'taiwan_player' }
);

module.exports = TaiwanPlayer;

// TeamTaiwan 跟 TaiwanPlayer 是一對多的關係

TeamTaiwan.hasMany(TaiwanPlayer);
TaiwanPlayer.belongsTo(TeamTaiwan);

新增 TaiwanPlayer 的 Foreign Key(team_taiwan_id),在程式裡會變成 teamTaiwanId

TaiwanPlayer.create({
  name: formData.name,
  teamTaiwanId: teamTaiwanId,
})
  .then(...)
  .catch(...);

有用到下面的 sequelize.fn, sequelize.col 語法時,取欄位值有二個方法:

TaiwanPlayer.findAll({
  attributes: [[sequelize.fn('COUNT', sequelize.col('name')), 'count_name']],
})

---

JSON.parse(JSON.stringify(data[0]))

或

data[0].get().count_name

網頁

使用 JS 實做分頁

參考 Laravel 5 的分頁模式,11 頁以下全部顯示,12 頁以上會出現省略的點點記號。

const page = document.querySelector('#page');

const pageNum = 16;

const urlParams = new URLSearchParams(window.location.search);

const activePage = urlParams.get('page');
console.log('activePage:', activePage);

// 顯示頁數 11 以下
if (pageNum <= 11) {
  let data = '';

  for (let i = 1; i <= pageNum; i++) {
    data += `<li class="page-item"><a class="page-link" href="/?page=${i}">${i}</a></li>`;
  }

  page.innerHTML = data;
}

// 顯示頁數 12 以上
if (pageNum > 11) {
  let data = '';

  if (activePage < 7) {
    for (let i = 1; i <= pageNum; i++) {
      if (i <= 8) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${i}">${i}</a></li>`;
      }

      if (i === pageNum - 2) {
        data += `<li class="page-item"><a class="page-link" href="javascript:;">...</a></li>`;
      }

      if (i === pageNum - 1) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${pageNum - 1}">${pageNum - 1}</a></li>`;
      }

      if (i === pageNum) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${pageNum}">${pageNum}</a></li>`;
      }
    }
  }

  if (activePage >= 7 && Number(activePage) + 6 <= pageNum) {
    for (let i = 1; i <= pageNum; i++) {
      if (i === 1 || i === 2) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${i}">${i}</a></li>`;
      }

      if (i === 3) {
        data += `<li class="page-item"><a class="page-link" href="javascript:;">...</a></li>`;
      }

      if (i >= activePage - 3 && i <= Number(activePage) + 3) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${i}">${i}</a></li>`;
      }

      if (i === pageNum - 2) {
        data += `<li class="page-item"><a class="page-link" href="javascript:;">...</a></li>`;
      }

      if (i === pageNum - 1) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${pageNum - 1}">${pageNum - 1}</a></li>`;
      }

      if (i === pageNum) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${pageNum}">${pageNum}</a></li>`;
      }
    }
  }

  if (activePage >= 7 && Number(activePage) + 6 > pageNum) {
    for (let i = 1; i <= pageNum; i++) {
      if (i === 1 || i === 2) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${i}">${i}</a></li>`;
      }

      if (i === 3) {
        data += `<li class="page-item"><a class="page-link" href="javascript:;">...</a></li>`;
      }

      if (i >= pageNum - 8) {
        data += `<li class="page-item"><a class="page-link" href="/?page=${i}">${i}</a></li>`;
      }
    }
  }

  page.innerHTML = data;
}

Demo

網頁

使用 express-validator

// router

const adminValidator = require('../validators/admin.validator');

...

router.post(
  '/shop-type/create',
  adminValidator.checkShopTypeFormData(),
  shopTypeController.createShopType
);
// admin.validator.js

const { body } = require('express-validator');

function checkShopTypeFormData() {
  return [body('name').trim().notEmpty().withMessage('請輸入商店類別')];
}

function checkShopFormData() {
  return [
    body('name').trim().notEmpty().withMessage('請輸入商店'),
    body('phone').trim().notEmpty().withMessage('請輸入電話'),
    body('note').optional().trim(),
    body('type').isNumeric().withMessage('請選擇類別'),
  ];
}

module.exports = { checkShopTypeFormData, checkShopFormData };
// controller

const { matchedData, validationResult } = require('express-validator');

...

const result = validationResult(req);

if (!result.isEmpty()) {
  ShopType.findAll()
    .then((shopTypeList) => {
      res.render('admin/shop/createShopForm', {
        errors: result.array(),
        old: req.body,
        shopTypeList,
      });
    })
    .catch((err) => {
      console.log(err);
    });
} else {
  const formData = matchedData(req);

  ShopType.findByPk(formData.type)
    .then((data) => {
      if (data === null) {
        res.send('404 Not Found');
        return;
      }

      return Shop.create({
        name: formData.name,
        phone: formData.phone,
        note: formData.note,
        shopTypeId: formData.type,
      });
    })
    .then((data) => {
      if (data) {
        res.redirect('/admin/shops');
      }
    })
    .catch((err) => {
      console.log(err);
    });
}
// view

<% if (errors.length > 0) { %>
<div>
  <% errors.forEach((err) => { %>
  <p><%= err.msg %></p>
  <% }) %>
</div>
<% } %>
網頁

[JS] Simple Calendar

參考 Learn How to Code a Simple JavaScript Calendar and Datepicker 這篇教學,做了一個簡單的月曆。

const previousMonth = document.querySelector('.previous-month');
const nextMonth = document.querySelector('.next-month');
const yearMonth = document.querySelector('.year-month');
const day = document.querySelector('.day');

let today = new Date();

// returns the full year of a date
// 4 digits
let year = today.getFullYear();

// returns the month (0 to 11) of a date
let month = today.getMonth();

showCalendar();

// 上個月
previousMonth.addEventListener('click', (e) => {
  e.preventDefault();

  month--;

  if (month < 0) {
    year--;
    month = 11;
  }

  showCalendar();
});

// 下個月
nextMonth.addEventListener('click', (e) => {
  e.preventDefault();

  month++;

  if (month > 11) {
    year++;
    month = 0;
  }

  showCalendar();
});

// function
function showCalendar() {
  yearMonth.innerHTML = `${year} / ${month + 1}`;

  // 當月第一天
  const firstDay = new Date(year, month, 1);

  // returns the day of the week (0 to 6) of a date
  // Sunday = 0, Monday = 1, ....
  const firstDayIndex = firstDay.getDay();

  // 當月最後一天
  const lastDay = new Date(year, month + 1, 0);

  // returns the day of the month (1 to 31) of a date
  const daysInMonth = lastDay.getDate();

  let dayHtml = '';

  for (let i = 1; i <= 42; i++) {
    if (i < firstDayIndex + 1) {
      dayHtml += '<div></div>';
    } else if (i > firstDayIndex + daysInMonth) {
      break;
    } else {
      const dayNumber = i - firstDayIndex;

      if (
        year === new Date().getFullYear() &&
        month === new Date().getMonth() &&
        dayNumber === new Date().getDate()
      ) {
        dayHtml += `<div class="current-date">${dayNumber}</div>`;
      } else {
        dayHtml += `<div>${dayNumber}</div>`;
      }
    }
  }

  day.innerHTML = dayHtml;
}

Demo

返回頂端