Skip to content

Commit

Permalink
feat: links (#282)
Browse files Browse the repository at this point in the history
  • Loading branch information
Tsuk1ko authored Oct 28, 2024
1 parent d6517b7 commit e77ce55
Show file tree
Hide file tree
Showing 11 changed files with 245 additions and 11 deletions.
102 changes: 102 additions & 0 deletions src/components/links/LinkCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<template>
<div class="mdui-card">
<div class="mdui-card-header">
<img class="mdui-card-header-avatar no-pe" :src="item.avatar" />
<div class="mdui-card-header-title">{{ item.name }}</div>
<div class="mdui-card-header-subtitle">{{ item.slogan }}</div>
</div>

<div class="mdui-card-content mdui-p-y-0">
<div class="link-card-tags">
<div v-for="(tag, i) of item.tags" :key="i" class="mdui-chip mdui-m-a-0">
<span class="mdui-chip-title">{{ tag }}</span>
</div>
</div>
<p>{{ item.description }}</p>
</div>

<div class="mdui-card-actions mdui-p-t-0">
<button
class="mdui-btn mdui-ripple"
v-for="(link, i) in item.links"
:key="i"
:class="link.primary ? 'mdui-text-color-theme-accent' : 'mdui-text-color-theme-secondary'"
@click="() => openLink(link)"
>{{ link.name }}</button
>
</div>
</div>
</template>

<script setup>
import { omit } from 'lodash';
import { computed } from 'vue';
import { useCeobeApiUtils } from '@/utils/ceobeCanteen';
const props = defineProps({
item: {
type: Object,
required: true,
},
});
const { getLocalizedText } = useCeobeApiUtils();
const item = computed(() => {
const data = props.item;
return {
name: getLocalizedText(data.localized_name),
description: getLocalizedText(data.localized_description),
slogan: getLocalizedText(data.localized_slogan),
tags: getLocalizedText(data.localized_tags),
avatar: data.icon_url,
links: data.links.map(link => ({
...omit(link, ['localized_name']),
name: getLocalizedText(link.localized_name),
})),
};
});
const openLink = ({ url }) => {
window.open(url, '_blank');
};
</script>

<style lang="scss" scoped>
.link-card-tags {
display: flex;
flex-wrap: wrap;
gap: 4px;
.mdui-chip {
cursor: default;
&:hover,
&:focus {
box-shadow: none;
}
}
}
.mdui-card {
display: flex;
flex-direction: column;
}
.mdui-card-header {
height: unset;
}
.mdui-card-header-subtitle {
white-space: normal;
}
.mdui-card-actions {
margin-top: auto;
}
.mdui-card-header-avatar {
object-fit: cover;
}
</style>
6 changes: 6 additions & 0 deletions src/data/changelog.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
[
{
"time": "2024-10-28",
"changes": [
"增加友情链接页面,感谢<a href=\"https://www.ceobecanteen.top/\" target=\"_blank\">小刻食堂</a>提供支持"
]
},
{
"time": "2024-08-27",
"changes": ["【精英材料计算】森空岛功能支持跟随多帐号切换"]
Expand Down
3 changes: 2 additions & 1 deletion src/locales/cn/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"material": "精英材料计算",
"level": "干员升级计算",
"riic": "基建技能筛选",
"depot": "仓库材料导入"
"depot": "仓库材料导入",
"links": "友情链接"
},
"setting": {
"rememberLastPage": "记住最后一次打开的选项卡",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/jp/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"material": "@:(common.operator)育成",
"level": "レベルアップ計算",
"riic": "基地スキル",
"depot": "倉庫のインポート"
"depot": "倉庫のインポート",
"links": "リンク集"
},
"setting": {
"rememberLastPage": "最後開いたページを記憶する",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/kr/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"material": "재료 계산기",
"level": "레벨 업",
"riic": "기반시설 스킬",
"depot": "창고 가져오기"
"depot": "창고 가져오기",
"links": "링크 모음"
},
"setting": {
"rememberLastPage": "마지막으로 열었던 탭 기억하기",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/tw/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"material": "精英材料計算",
"level": "幹員升級計算",
"riic": "基建技能篩選",
"depot": "倉庫材料導入"
"depot": "倉庫材料導入",
"links": "友情連結"
},
"setting": {
"rememberLastPage": "記住最後一次打開的索引標籤",
Expand Down
3 changes: 2 additions & 1 deletion src/locales/us/app.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
"material": "Material Calculation",
"level": "Level Up",
"riic": "RIIC Skills",
"depot": "Depot Import"
"depot": "Depot Import",
"links": "Links"
},
"setting": {
"rememberLastPage": "Remember last tab opened",
Expand Down
1 change: 1 addition & 0 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ new Vue({
provide() {
return {
isReleasedChar: this.isReleasedChar,
getRoot: () => this,
};
},
data: {
Expand Down
20 changes: 14 additions & 6 deletions src/router.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ export const router = new Router({
{
path: '/material',
name: 'material',
component: waitDataReady(() =>
import(/* webpackChunkName: "app.material" */ './views/Material/index.vue'),
component: waitDataReady(
() => import(/* webpackChunkName: "app.material" */ './views/Material/index.vue'),
),
},
{
path: '/level',
name: 'level',
component: waitDataReady(() =>
import(/* webpackChunkName: "app.level" */ './views/Level.vue'),
component: waitDataReady(
() => import(/* webpackChunkName: "app.level" */ './views/Level.vue'),
),
},
{
Expand All @@ -58,10 +58,15 @@ export const router = new Router({
{
path: '/depot',
name: 'depot',
component: waitDataReady(() =>
import(/* webpackChunkName: "app.depot" */ './views/Depot.vue'),
component: waitDataReady(
() => import(/* webpackChunkName: "app.depot" */ './views/Depot.vue'),
),
},
{
path: '/links',
name: 'links',
component: () => import(/* webpackChunkName: "app.links" */ './views/Links.vue'),
},
],
});

Expand Down Expand Up @@ -90,4 +95,7 @@ export const meta = {
icon: 'apps',
chip: 'v1',
},
links: {
icon: 'link',
},
};
13 changes: 13 additions & 0 deletions src/utils/ceobeCanteen.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { computed, inject } from 'vue';

export const useCeobeApiUtils = () => {
const isLocaleZH = computed(() => inject('getRoot')?.()?.$root.localeZH);

const getLocalizedText = obj =>
obj[isLocaleZH.value ? 'zh_CN' : 'en_US'] || obj.zh_CN || obj.en_US || '';

return {
isLocaleZH,
getLocalizedText,
};
};
99 changes: 99 additions & 0 deletions src/views/Links.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<template>
<div id="recommended-links">
<div class="link-list">
<LinkCard v-for="item in sortedLinkList" :key="item.id" :item="item" />
</div>
<div v-show="sortedLinkList.length" class="mdui-m-t-2">
<small class="mdui-typo"
>Powered by <a href="https://www.ceobecanteen.top/" target="_blank">Ceobe Canteen</a></small
>
</div>
</div>
</template>

<script setup>
import { onMounted, shallowRef, computed } from 'vue';
import { isEqual } from 'lodash';
import { createInstance } from 'localforage';
import LinkCard from '@/components/links/LinkCard.vue';
import { useCeobeApiUtils } from '@/utils/ceobeCanteen';
const { getLocalizedText } = useCeobeApiUtils();
const storage = createInstance({ name: 'recommended-links' });
const linkList = shallowRef([]);
const sortedLinkList = computed(() =>
[...linkList.value].sort((a, b) =>
getLocalizedText(a.localized_name).localeCompare(getLocalizedText(b.localized_name)),
),
);
onMounted(() => {
initLinkList();
});
const initLinkList = async () => {
const { data, timestamp } = await storage.getItems(['data', 'timestamp']);
if (Array.isArray(data)) {
setLinkList(data);
}
if (!Array.isArray(data) || !data.length || timestamp < Date.now() - 3600e3) {
await fetchToolLinks();
}
};
const fetchToolLinks = async () => {
const { code, message, data } = await fetch(
'https://server-cdn.ceobecanteen.top/api/v1/cdn/operate/toolLink/list',
).then(r => r.json());
if (!Array.isArray(data) || !data.length) {
if (Number(code) !== 0) throw new Error(`(${code})${message}`);
console.warn('[FetchToolLinks] no data');
return;
}
storage.setItem('data', data);
storage.setItem('timestamp', Date.now());
setLinkList(data);
};
const setLinkList = data => {
const myId = '8be8bfc0-43ed-4bbc-b6da-450804f2bd5b';
const ceobeCanteenId = '7811feb6-b473-4ee7-b83c-c8fabe4d1c4d';
const ceobeCanteenSlogan = {
zh_CN: '赋能小刻,万物皆为饼',
en_US: 'Empower Ceobe, where everything is a cookie',
};
const emptySlogan = {
zh_CN: '',
en_US: '',
};
linkList.value = data
.filter(item => item.id !== myId)
.map(item =>
item.id !== ceobeCanteenId && isEqual(item.localized_slogan, ceobeCanteenSlogan)
? { ...item, localized_slogan: emptySlogan }
: item,
);
};
</script>

<style lang="scss" scoped>
.link-list {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
gap: 24px;
}
@media screen and (max-width: 600px) {
.link-list {
grid-template-columns: 1fr;
}
}
</style>

0 comments on commit e77ce55

Please sign in to comment.