6. Cấu trúc đề tài
3.1.2 Trang Quản trị viên
- Giao diện trang đăng nhập
Chức năng dành cho quản trị viên khi muốn thực hiện quản lý Website. Quản trị viên sẽ nhập tên và mật khẩu tương ứng sau đó nhấn Login trên màn hình, hệ thống tiến hành kiểm tra tài khoản. Nếu đúng, chuyển sang trang quản trị, còn nếu sai, hệ thống sẽ yêu cầu nhập lại.
- Giao diện trang quản trị
Sau khi đăng nhập thành công, giao diện trang quản trị hiển thị với danh sách tất cả phim. Tại đây, quản trị viên thực hiện các chức năng quản lý phim như: Thêm phim mới, sửa thông tin phim và xóa phim.
111 - Giao diện chức năng quản lý rạp
Danh sách các rạp sẽ được hiển thị tại phần Danh mục rạp. Tại đây, quản trị viên thực hiện các chức năng như: Thêm rạp, sửa thông tin rạp và xóa rạp.
112
- Giao diện chức năng quản lý trạng thái của phim
Trạng thái của phim gồm: Phim hot, phim mới, phim đang chiếu và phim sắp chiếu. Mỗi danh mục đều có các chức năng và cách xử lý để quản trị viên có thể quản lý tại phần Danh mục phim.
- Giao diện chức năng quản lý diễn viên
Danh sách các diễn viên sẽ được hiển thị tại phần Diễn viên. Tại đây, quản trị viên thực hiện các chức năng như: Thêm diễn viên, sửa thông tin diễn viên và xóa diễn viên.
113
3.2 Kết luận chương
Sau quá trình phân tích, thiết kế và xây dựng hệ thống giới thiệu phim mới của rạp chiếu phim trên nền Web, chương 3 đã demo được giao diện và các chức năng của sản phẩm.
114
PHỤ LỤC
1.Một số đoạn code xây dựng Front-end ở chương 2
- Component xây dựng giao diện trang phim chi tiết:
+ Component phần diễn viên:
import React, {Component} from 'react';
import defaultImage from '../../asset/images/default-image.jpg'; export default class ActorComponent extends Component {
render() {
const fillData = this.props.filmDetail ? this.props.filmDetail.Actors : [];
let showData = fillData &&
fillData.map((item, key) => {
const actorAvatar = item.Image?.Url ? item.Image.Url : defaultImage; return (
<div className="actor" key={key}>
<div className="actor-detail">
<p>Tên diễn viên: {item.ActorName}</p>
<p>Tuổi: {item.Age}</p>
</div>
<div className="actor-img">
<div style={{ backgroundImage: `url(${actorAvatar})`, backgroundPosition: 'center', backgroundSize: 'cover', }} ></div> </div> </div> ); }); return (
<div className="film-actor" id="style-scroll-bar">
{showData}
</div>
); } }
+ Component phần thông tin phim: <div className="poster">
<div className="poster-left">
<div className="poster-img">
<img src={this.state.PosterImage?.Url} alt="poster" width="100%" />
115
</div>
<div className="poster-detail" id="style-scroll-bar">
<p>Đạo diễn: {this.state.Director}</p>
<p>Biên kịch: {this.state.Writer}</p>
<p>Nhà sản xuất: {this.state.Production}</p>
<p>Quốc gia: {this.state.National}</p>
<p>Ngày ra rạp: {dateTime && dateTime[0]}</p>
<p>Thời lượng: {this.state.RunningTime} phút</p>
<p>Độ tuổi: {this.state.Rated}</p>
<p>
Thể loại:{' '}
{this.state.Genres &&
this.state.Genres.map((genres, keyGenres) => (
<span key={keyGenres} style={{display: 'inline-block'}}>
{`${String.fromCharCode(160)}${genres}`}, </span>
))}
</p>
{this.state.Score && ( <span>
<span style={{display: 'block'}}>
-IMDB: {this.state.Score[0].IMDB}
</span>
<span style={{display: 'block'}}>
-RottenTomato: {this.state.Score[0].RottenTomato}
</span>
</span>
)}
</div>
</div>
+ Component phẩn review phim:
const reviewImage = this.props.filmDetail?.ReviewImage; let reviewContent;
if (this.props.filmDetail) { reviewContent = {
__html: marked(this.props.filmDetail?.ReviewContent), }; } return ( <div className="film-review" style={{
backgroundImage: `url(${reviewImage && reviewImage.Url})`, backgroundRepeat: 'no-repeat',
backgroundSize: 'cover', backgroundPosition: 'center', }}
116 <p>review :</p> <div className={'film-content'} dangerouslySetInnerHTML={reviewContent} ></div>
<span className="overlay"></span>
</div>
);
- Sử dụng CSS để xây dựng giao diện:
.film-detail .actor-title { display: flex; flex-direction: column; align-items: flex-end; padding: 0 150px; } .film-detail .actor-title h2 { color: #fff; padding: 0 5px 10px 30vw; border-bottom: 1px solid #4d4d4d; margin-bottom: 50px; width: 100%; text-align: right; } .film-detail .film-content { display: flex; width: 100%; justify-content: space-between; padding: 0 150px; }
.film-detail .film-content .film-review {
flex-basis: 50%; text-align: left; display: flex; flex-direction: column; justify-content: center; padding: 50px 100px; height: 100vh; overflow: auto; align-items: center; position: relative; border-radius: 10px; }
.film-detail .film-content .film-review .overlay { position: absolute; top: 0; bottom: 0; right: 0; left: 0; background: rgba(21, 22, 23, 0.55); z-index: 1; border-radius: 10px; }
117 - Reducer của trang phim chi tiết:
const DetailFilmReducer = (state = DEFAULT_STATE, action = {}) => { switch (action.type) {
case Constants.GET_FILM_DETAIL_REQUEST: return {
...state,
isFetching: true, };
case Constants.GET_FILM_DETAIL_SUCCESS: return { ...state, isFetching: false, dataFetched: true, error: false, errorMessage: null,
filmDetail: action.payload, };
case Constants.GET_FILM_DETAIL_FAILURE: return {
...state,
isFetching: false, error: true,
errorMessage: action.payload, }; default: return state; } }; - Xử lý trong Saga:
function* getFilmDetail(action) { try {
const dataSend = {
path: `/film/${action.payload}`, method: types.GET,
};
const result = yield getFilmDetailAPI(dataSend); yield put(actions.getFilmDetailSuccess(result)); } catch (error) {
yield put(actions.getFilmDetailFailure(error.message)); }
}
takeEvery(types.GET_FILM_DETAIL_REQUEST, getFilmDetail),
- Gọi API trong file Fetch API:
export default function getFilmDetailAPI(data) { const objFetch = {
118
};
return new Promise((resolve, reject) => { const url = Constants.DOMAIN + data.path
fetch(url, objFetch).then((res) => resolve(res.json())) .catch((err) => reject(err))
}) }
2.Một số đoạn code xây dựng Back-end ở chương 2
- Một số API đc tạo tại file Route:
+ API về phim:
app.route('/films').get(film_controller.get_film); app
.route('/film')
.post(film_controller.add_film)
.delete(film_controller.delete_film);
app.route('/film/live').delete(film_controller.delete_in_theater_film); app.route('/film/search').get(film_controller.search_film);
app
.route('/film/:film_id')
.get(film_controller.get_film_by_id) .put(film_controller.update_film);
app.route('/film/add_hot').post(film_controller.update_hot_film); app.route('/film/add_new').post(film_controller.update_new_film);
app
.route('/film/film_image/:film_id') .post(film_controller.upload_film_image) .put(film_controller.upload_film_image);
- Xử lý trong Controller:
+ Thêm phim:
exports.add_film = async (req, res) => { req.files.map(async (file) => {
if (file.fieldname === 'filmData') {
const newFilm = JSON.parse(fs.readFileSync(file.path)); fs.unlinkSync(file.path);
console.log(newFilm);
const film = await Film.create(newFilm);
res.redirect(307, `/film/film_image/${film._id}`); }
}); };
+ Cập nhật phim:
119
let updateFilm;
const id = req.params.film_id; req.files.map((file) => {
if (file.fieldname === 'filmData') {
updateFilm = JSON.parse(fs.readFileSync(file.path)); fs.unlinkSync(file.path);
} });
Film.findByIdAndUpdate(id, updateFilm, { new: true, useFindAndModify: fals e })
.then(() => {
res.redirect(307, `/film/film_image/${id}`); })
.catch((err) => { console.log(err); });
};
+ Xóa phim:
exports.delete_film = (req, res) => { const listId = req.body;
const deletePromise = listId.map( (filmId) =>
new Promise((resolve, reject) => {
Film.findByIdAndUpdate(filmId, { isDeleted: true }, { new: true }) .then((result) => { resolve(result); }) .catch((err) => { reject(err); }); }), );
Promise.all(deletePromise) .then((result) => { res.send({ result }); })
.catch((err) => { res.send({ err }); });
};
- Mô phỏng bảng với file Model:
+ Bảng về phim
var mongoose = require('mongoose');
120 var FilmSchema = new Schema({
FilmName: { type: String,
required: [true, 'nhap ten phim'], }, Director: { type: String, }, Writer: { type: String, }, Production: { type: String, }, Actors: [ {
type: Schema.Types.ObjectId, ref: 'actor',
require: [true, 'Khong co dien vien'], },
],
Cinemas: [ {
type: Schema.Types.ObjectId, ref: 'cinemaCluster',
require: [true, 'Khong co rap'], },
],
Genres: [ {
type: String,
require: [true, 'Khong co the loai'], },
],
RunningTime: { type: Number,
required: [true, 'Nhap thoi gian phim'], },
ReleaseDate: { type: Date,
required: [true, 'nhap ngay ra mat nao'], },
ReviewContent: { type: String,
required: [true, 'Khong co review a ????'], },
121
type: String, },
CoverImage: {
type: Schema.Types.ObjectId, ref: 'image',
default: null, },
PosterImage: {
type: Schema.Types.ObjectId, ref: 'image',
require: [true, 'anh dau ???'], },
Images: [ {
type: Schema.Types.ObjectId, ref: 'image',
default: null, },
],
ReviewImage: {
type: Schema.Types.ObjectId, ref: 'image', }, Score: [ { IMDB: { type: String, }, RottenTomato: { type: String, }, }, ], TrailerUrl: { type: String, require, }, National: { type: String, }, isHotFilm: { type: Boolean, default: false, }, isNewFilm: { type: Boolean, default: false, },
122 isShowing: { type: Boolean, default: true, }, isDeleted: { type: Boolean, default: false, }, });
123
KẾT LUẬT VÀ HƯỚNG PHÁT TRIỂN 1. Kết luận
Công nghệ là lĩnh vực luôn thay đổi và phát triển, những công nghệ mới luôn được cập nhật với những tính năng vượt trội hơn. Đối với công nghệ thông tin nói riêng, đòi hỏi các lập trình viên phải luôn trau dồi các kiến thức mới, tìm ra phương pháp tối ưu để phù hợn với xu hướng xã hội. Chính vì thế việc áp dụng mô hình Web service và Web API với React trên nền tảng NodeJS, kết hợp với công nghệ NoSQL – MongoDB đang là xu thế hiện nay và được đánh giá cao bởi các nhà phát triển vì những tính năng mà chúng đem lại.
Đi đôi với sự phát triển của công nghệ chính là sự phát triển về đời sống con người. Nhu cầu giải trí tăng, rạp chiếu phim trở thành sự lựa chọn không thể thiếu. Vì vậy, để người xem có lựa chọn tốt nhất và phù hợp nhất với nhu cầu của bản thân, hệ thống giới thiệu phim mới của rạp chiếu phim trở nên cần thiết. Để tiếp cận dễ dàng với người dùng trong sự bùng nổ về Internet hiện nay thì nền tảng Web là sự lựa chọn mà tôi hướng tới.
- Những điểm đạt được:
Giao diện bắt mắt, thân thiện với người dùng, các chức năng được thể hiện rõ rang, dễ nắm bắt.
Xây dựng Front-end bằng React nên việc phân chia các Component giúp giao diện trở nên dễ quản lý đối với lập trình viên.
Đã xây dựng được các chức năng cơ bản của một Website giới thiệu phim với những thông tin cần thiết giúp người dùng có lựa chọn hợp với nhu cầu của bản thân.
Đối với quản trị viên, các chức năng quản lý phim, rạp và những danh mục liên quan thuận tiện, dễ sử dụng.
- Những điểm hạn chế:
Tuy đã rất cố gắng và nhận được sự giúp đỡ tận tình của giảng viên hướng dẫn, song do thời gian, trình độ và kinh phí có hạn nên Website mà tôi xây dựng vẫn còn tồn tại một số hạn chế như sau:
124
Chưa có chức năng để người dùng có thể tương tác, đánh giá và bình luận về phim.
Các chức năng admin còn tồn tại hạn chế, chưa thực sự linh động trong việc xử lý.
2. Hướng phát triển
Trong thời gian tới tôi sẽ tiếp tục trau dồi kiến thức về các công nghệ lập trình nói chung và React, NodeJS, MongoDB nói riêng, nghiên cứu các phương pháp tối ưu để xây dựng hoàn chỉnh một Website giới thiệu phim với các chức năng cần thiết. Ngoài ra, Website sẽ mỡ rộng với chức năng viết bài để người dùng khi đăng nhập vào hệ thống có thể viết những bài cảm nhận, đánh giá về phim. Từ đó, giúp Website có thể đưa vào hoạt động và phát triển.
125
TÀI LIỆU THAM KHẢO
[1], w3schools.com [2], nodejs.org [3], reactjs.org [4], mongodb.com [5], cucdienanh.vn
[6], Trần Thị Song Minh (2019), Hệ thống thông tin quản lý – Nhà xuất bản Đại học Kinh tế quốc dân
[7], Đoàn Văn Ban, Nguyễn Thị Tĩnh (2012), Phân tích thiết kế hướng đối tượng bằng UML, Nhà xuất bản Đại học Sư phạm
[8], Anthony Accomazzo, Ari Lerner, Nate Murray, Clay Allsopp, David Gutman và Tyler McGinnis (2017), Fullstack React The Complete Guide to ReactJS and Friends, Được xuất bản tại San Francisco - California bởi Fullstack.io