使用 Vue 2.5 實作「相片藝廊」應用程式

設定檔

在根目錄新增 .env.local 檔,代表本地環境:

1
2
3
4
VUE_APP_API_URL="http://admin.cpac.test/api"
VUE_APP_CACHE_MINUTES_CATEGORIES=43200 # 1 month
VUE_APP_CACHE_MINUTES_ALBUMS=10080 # 1 week
VUE_APP_CACHE_MINUTES_PHOTOS=1440 # 1 day

在根目錄新增 .env.production.local 檔,代表生產環境:

1
2
3
4
VUE_APP_API_URL="http://archives.cpac.epoch.tw/api"
VUE_APP_CACHE_MINUTES_CATEGORIES=43200 # 1 month
VUE_APP_CACHE_MINUTES_ALBUMS=10080 # 1 week
VUE_APP_CACHE_MINUTES_PHOTOS=1440 # 1 day

取得設定檔的值:

1
const minutes = parseInt(process.env.VUE_APP_CACHE_MINUTES_ALBUMS, 10);

設定 Axios 預設 API 網址

修改 src/main.js 檔:

1
2
Vue.use(VueAxios, axios);
axios.defaults.baseURL = process.env.VUE_APP_API_URL;

狀態管理器

將 Vuex 模組化。

修改 src/store/index.js 檔:

1
2
3
4
5
6
7
8
9
10
11
import Vue from 'vue';
import Vuex from 'vuex';
import gallery from './modules/gallery';

Vue.use(Vuex);

export default new Vuex.Store({
modules: {
gallery,
},
});

新增 src/store/modules/gallery.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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
import axios from 'axios';
import Cache from '../../helpers/Cache';

export default {
state: {
categories: [],
category: '',
albums: [],
album: null,
photos: [],
photo: null,
},
mutations: {
setCategories(state, categories) {
state.categories = categories;
},
setCategory(state, category) {
state.category = category;
},
setAlbums(state, albums) {
state.albums = albums;
},
setAlbum(state, album) {
state.album = album;
},
setPhotos(state, photos) {
state.photos = photos;
},
setPhoto(state, photo) {
state.photo = photo;
},
},
actions: {
fetchCategories(context, { resource, minutes }) {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: resource.url,
})
.then(({ data }) => {
Cache.set(resource, data.data, minutes);
context.commit('setCategories', data.data);
resolve(data);
})
.catch((error) => {
reject(error);
});
});
},
fetchAlbums(context, { resource, minutes }) {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: resource.url,
params: resource.params,
})
.then(({ data }) => {
Cache.set(resource, data.data, minutes);
context.commit('setAlbums', data.data);
resolve(data);
})
.catch((error) => {
reject(error);
});
});
},
fetchPhotos(context, { resource, minutes }) {
return new Promise((resolve, reject) => {
axios({
method: 'GET',
url: resource.url,
params: resource.params,
})
.then(({ data }) => {
Cache.set(resource, data.data, minutes);
context.commit('setPhotos', data.data);
resolve(data);
})
.catch((error) => {
reject(error);
});
});
},
},
};

輔助函式

新增 src/helpers/Cache.js 檔,用以存取快取資料:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Cache {
static set(key, value, minutes) {
localStorage.setItem(JSON.stringify(key), JSON.stringify({
created_at: Date.now(), // 建立時間
expires_in: minutes, // 存活時間
data: value, // 存入資料
}));
}

static get(key) {
const cache = JSON.parse(localStorage.getItem(JSON.stringify(key)));
if (!cache) {
return null;
}
if (Date.now() - cache.created_at > cache.expires_in * 60 * 1000) {
return null;
}
return cache.data;
}
}

export default Cache;

在元件存取狀態管理器

取得狀態:

1
2
3
4
5
6
7
8
9
10
11
12
13
export default {
computed: {
category() {
return this.$store.state.gallery.category;
},
album() {
return this.$store.state.gallery.album;
},
photo() {
return this.$store.state.gallery.photo;
},
},
};

提交狀態:

1
2
3
4
5
6
7
8
9
10
11
12
13
export default {
methods: {
setCategory(value) {
this.$store.commit('setCategory', value);
},
setAlbum(value) {
this.$store.commit('setAlbum', value);
},
setPhoto(value) {
this.$store.commit('setPhoto', value);
},
},
};

註冊事件:

1
2
3
4
5
6
7
8
9
10
export default {
methods: {
fetchAlbums(resource) {
this.$store.dispatch('fetchAlbums', { resource, minutes })
.then(() => {
this.albums = this.$store.state.gallery.albums;
});
},
},
};

修改 Vuetify 樣式

修改 src/plugins/vuetify.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
import Vue from 'vue';
import Vuetify from 'vuetify';
import colors from 'vuetify/lib/util/colors';
import zhHant from 'vuetify/es5/locale/zh-Hant';
import 'material-design-icons-iconfont/dist/material-design-icons.css';
import '@fortawesome/fontawesome-free/css/all.css';

Vue.use(Vuetify, {
theme: {
primary: colors.indigo,
secondary: '#424242',
accent: '#82B1FF',
error: '#FF5252',
info: colors.orange,
success: '#4CAF50',
warning: '#FFC107',
},
customProperties: true,
iconfont: 'fa',
lang: {
locales: { zhHant },
current: 'zh-Hant',
},
});

新增 src/stylus/main.styl 檔,以變更預設字體:

1
2
3
4
5
@import '~vuetify/src/stylus/settings/_theme'

$body-font-family = 'Microsoft Jhenghei'

@import '~vuetify/src/stylus/main'

src/main.js 引入相關檔案:

1
2
import './plugins/vuetify';
import './stylus/main.styl';

程式碼