iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
Modern Web

30天打造個人簡易旅遊網站系列 第 24

Day 24:使用Redux建立一個旅遊行程(三)

  • 分享至 

  • xImage
  •  

在昨天我們已經可以新增一個行程了,在今天我們要實現把景點加入到該行程的功能,那一開始我們一樣先定義一下redux架構。

1.定義Redux架構:

新增一個addToSchedule.js的的檔案,那store.js一樣記得做匯入reducer的動作,這邊不多做解釋,想要復習的朋友可以去前幾天的文章復習。我們先使用一個範例的行程確認是否能成功讀取到,再來定義addSchedule與removeSchedule兩個actiion。

import { createSlice } from '@reduxjs/toolkit';
const initialState = {
    schedules: [{
        scheduleId: '1',
        scheduleName: '範例行程',
        time: '2024-10-05',
        landmarks: [],
    },]
};
const addToScheduleSlice = createSlice({
    name: 'addToSchedules',
    initialState,
    reducers: {
        addSchedule: (state, action) => {
            const { 
                scheduleName, 
                scheduleId, 
                time, 
                landmarks,
                landmark } = action.payload;
            let isDuplicate = state.schedules.find(
                        item => item.scheduleName === scheduleName);
            if (!state.schedules) {
                console.error("State schedules is undefined");
                return;
            }
            if (!isDuplicate) {
                state.schedules.push({
                    scheduleId,
                    scheduleName,
                    time,
                    landmarks,
                });
            } else {
                console.log('行程名稱重複,無法添加');
                const existingSchedule = state.schedules.find(
                        schedule => schedule.scheduleName === scheduleName);
                if (existingSchedule) {
                    // 檢查是否已存在相同的地標,避免重複添加
                    const existingLandmark = existingSchedule.landmarks.find(
                            l => l.name === landmark.name);
                    if (!existingLandmark) {
                        // 如果沒有找到相同的 landmark,添加新的 landmark
                        existingSchedule.landmarks.push(landmark);
                    }
                    // 如果地標已存在,則不進行任何操作
                } else {
                    // 如果沒有找到對應的 scheduleName,新增一個新項目
                    state.schedules.push({
                        scheduleName: scheduleName,
                        landmarks: [landmark] 
                    });
                }
            }
        },
        removeSchedule: (state, action) => {
            const { scheduleName, landmarkName } = action.payload;
            const scheduleIndex = state.schedules.findIndex(
                    schedule => schedule.scheduleName === scheduleName);
            if (scheduleIndex !== -1) {
                const landmarks = state.schedules[scheduleIndex].landmarks;
                const filteredLandmarks = landmarks.filter(
                        landmark => landmark.name !== landmarkName);
                state.schedules[scheduleIndex].landmarks = filteredLandmarks;
            }
        },
    },
});
// export state to global
export const selectScheduleName = (state) => state.addToSchedule.schedules;
export const { addSchedule, removeSchedule } = addToScheduleSlice.actions;
export default addToScheduleSlice.reducer;

2.修改建立行程的ScheduleModal:

在建立行程這裡,我們要更改一下他的架構,除了addScheduleItems之外,我們還要建立一個可以裝入景點資訊的action,其實最後addScheduleItems是用不到的,只是先保留起來確認行程是有成功建立的,架構也比較簡單,如果想刪掉也是可以的。那這邊也用到一個新套件叫uuid的套件,可以隨機給出一個id到我們想要的物件裡,以免出錯。一樣用npm install uuid安裝。

import { addScheduleItems } from '../redux/scheduleSlice';
import { addSchedule } from '../redux/addToSchedule';
import { v4 as uuidv4 } from 'uuid';

const handleOk = () => {
    form.validateFields().then(values => {

        dispatch(addSchedule({
            scheduleId: uuidv4(),
            scheduleName: scheduleName,
            time: time,
            landmarks
        }));

        dispatch(addScheduleItems({
            scheduleName: scheduleName,
            time: time,
        }));
};

3.建立AddToSchedule:

在這邊利用selectScheduleName讀取已建立的行程,若沒有已建立的行程則會顯示”請先建立行程!”,當我們按下ok按鈕後,會透過dispatch將這個景點加入到此行程中,那加入的動作是用addSchedule來執行,那在這邊也會定義要回傳的資料的架構。而我們之前也用了防止重複加入的功能,所以一個景點只能出現在一個行程中一次。
useDispatch 用來分發 (dispatch) Redux actions。useSelector 從 Redux store 中提取selectScheduleName,並預設為空陣列。如果行程項目為空,會顯示提示訊息 "請先建立行程"。

export default function AddToSchedule({ landmark }) {
    const [open, setOpen] = useState(false);
    const showModal = () => {
        setOpen(true);
        console.log('scheduleItem:',scheduleItem)
    };
    const handleCancel = () => {
        setOpen(false);
    };

    const [value, setValue] = useState(1);
    const dispatch = useDispatch();
    const scheduleItem = useSelector(selectScheduleName) || [];
    const [selectedSchedule, setSelectedSchedule] = useState('');

    const onChange = (e) => {
        const newSchedule = e.target.value;
        setSelectedSchedule(newSchedule);
        setValue(e.target.value);
    };

    const handleOk = () => {
        if (scheduleItem.length > 0) {
            addToSche();
        }
        notification.success({
            message: '成功新增景點!',
            description: `已添加 ${landmark.name} 到${selectedSchedule}中`,
            placement: 'top'
        });
    };

    const addToSche = () => {
        dispatch(addSchedule({
            scheduleName: selectedSchedule,
            landmark: {
                name: landmark.name,
                id: landmark.id,
                image: landmark.image,
                position: landmark.position,
                description: landmark.description,
                end_time: landmark.end_time,
                qty: 1,
            },
        }));
    };

    return (
        <div>
            <Button type="primary" className={styles.addbox} onClick={showModal}>
                加入行程
            </Button>

            <Modal
                title="加入行程"
                centered
                open={open}
                onOk={handleOk}
                okType="default"
                onCancel={handleCancel}
            >
                {scheduleItem.length === 0 ? (<div>請先建立行程!</div>) : (
                    <Radio.Group onChange={onChange} value={value}>
                        <Space direction="vertical">
                            {scheduleItem.map((item, index) => (
                                <Radio key={index} value={item.scheduleName}>
                                   {item.scheduleName}
                                </Radio>
                            ))}
                        </Space>
                    </Radio.Group>)}
            </Modal>
        </div>
    );
}

4.實際畫面:

這邊可以看到我們可以成功將景點加入到已建立的行程。
https://ithelp.ithome.com.tw/upload/images/20241007/20169447UZhWgvJalR.png
https://ithelp.ithome.com.tw/upload/images/20241007/2016944782lBligsr4.png

5.總結:

今天更改了schedule的架構,使他可以裝入景點資訊,而我們也做了防呆機制,避免重複加入同個景點到同個行程中,其他架構都跟加入收藏差不多,有興趣的朋友們可以到<Day 21:使用Redux完成收藏功能>中去複習一下喔。下一篇,我們會將建立的行程放到其他頁面顯示,然後在裡面做更換景點順序的功能,就請各位敬請期待了!


上一篇
Day 23:使用Redux建立一個旅遊行程(二)
下一篇
Day 25:Draggable-拖曳畫面中的物件
系列文
30天打造個人簡易旅遊網站30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言