Skip to content

Commit

Permalink
feat: 订阅支持标签分组
Browse files Browse the repository at this point in the history
  • Loading branch information
xream committed Mar 17, 2024
1 parent 6646778 commit 0e1066c
Show file tree
Hide file tree
Showing 8 changed files with 113 additions and 11 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "sub-store-front-end",
"version": "2.14.166",
"version": "2.14.169",
"private": true,
"scripts": {
"dev": "vite --host",
Expand Down
7 changes: 7 additions & 0 deletions src/components/SubListItem.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@
<div class="sub-item-title-wrapper">
<h3 v-if="!isSimpleMode" class="sub-item-title">
{{ displayName || name }}
<span class="tag" v-for="i in tag" :key="i"><nut-tag>{{ i }}</nut-tag></span>
</h3>
<h3 v-else class="sub-item-title" style="color: var(--primary-text-color); font-size: 16px">
{{ displayName || name }}
<span class="tag" v-for="i in tag" :key="i"><nut-tag>{{ i }}</nut-tag></span>
</h3>

<!-- onClickCopyLink 拷贝 -->
Expand Down Expand Up @@ -262,6 +264,7 @@ const displayName =
props[props.type].displayName || props[props.type]["display-name"];
const name = props[props.type].name;
const tag = props[props.type].tag;
const { flows } = storeToRefs(subsStore);
const icon = computed(() => {
return isDefaultIcon.value ? logoIcon : logoRedIcon;
Expand Down Expand Up @@ -612,6 +615,10 @@ const onClickRefresh = async () => {
color: var(--primary-text-color);
}
.tag {
margin: 0 2px;
}
.copy-sub-link,
.refresh-sub-flow {
background-color: transparent;
Expand Down
5 changes: 5 additions & 0 deletions src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default {
unknownType: 'Unknown Type',
unknownSource: 'Unknown Source',
unknown: 'Unknown',
all: 'All',
},
globalNotify: {
refresh: {
Expand Down Expand Up @@ -208,6 +209,10 @@ export default {
label: 'Display Name',
placeholder: 'The display name',
},
tag: {
label: 'Tag(s)',
placeholder: 'The tag(s) (separated by comma) will be used for grouping.',
},
source: {
label: 'Source',
remote: 'Remote URL',
Expand Down
5 changes: 5 additions & 0 deletions src/locales/zh.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default {
unknownType: '未知类型',
unknownSource: '未知来源',
unknown: '未知',
all: '全部',
},
globalNotify: {
refresh: {
Expand Down Expand Up @@ -208,6 +209,10 @@ export default {
label: '显示名称',
placeholder: '输入展示的名称',
},
tag: {
label: '标签',
placeholder: '标签(用 , 分隔) 将用于分组',
},
source: {
label: '来源',
remote: '远程订阅',
Expand Down
14 changes: 12 additions & 2 deletions src/store/subs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,20 @@ export const useSubsStore = defineStore('subsStore', {
async fetchSubsData() {
await Promise.all([subsApi.getSubs(), subsApi.getCollections(), filesApi.getWholeFiles()]).then(res => {
if ('data' in res[0].data) {
this.subs = res[0].data.data;
this.subs = res[0].data.data.map(i => {
if (!Array.isArray(i.tag)) {
i.tag = []
}
return i
});
}
if ('data' in res[1].data) {
this.collections = res[1].data.data;
this.collections = res[1].data.data.map(i => {
if (!Array.isArray(i.tag)) {
i.tag = []
}
return i
});
}
if ('data' in res[2].data) {
this.files = res[2].data.data;
Expand Down
2 changes: 2 additions & 0 deletions src/types/store/subsStore.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface Sub {
ua?: string;
mergeSources?: string;
subUserinfo?: string;
tag?: string[];
process: Process[];
}

Expand All @@ -46,6 +47,7 @@ interface Collection {
process: Process[];
subscriptions: string[];
icon?: string;
tag?: string[];
}

interface Artifacts {
Expand Down
57 changes: 51 additions & 6 deletions src/views/Sub.vue
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,15 @@
<!-- 页面内容 -->
<!-- 有数据 -->
<div class="subs-list-wrapper">
<div v-if="tags && tags.length > 0" class="radio-wrapper" >
<nut-radiogroup v-model="tag" direction="horizontal">
<nut-radio v-for="i in tags" shape="button" :label="String(i.value)">{{ i.label }}</nut-radio>
</nut-radiogroup>
</div>
<div v-if="hasSubs">
<div class="sticky-title-wrappers">
<p class="list-title" @click="toggleSubFold">
<p>{{ $t(`specificWord.singleSub`) }}</p>
<p>{{ $t(`specificWord.singleSub`) + '('+subs.length+')' }}</p>
<nut-icon v-if="!isSubFold" name="rect-down" size="12px"></nut-icon>
<nut-icon v-else name="rect-right" size="12px"></nut-icon>
</p>
Expand All @@ -115,7 +120,7 @@
@end="handleDragEnd(subs)"
>
<template #item="{ element }">
<div :key="element.name" class="draggable-item">
<div :key="element.name" class="draggable-item" v-show="tag === 'all' || element.tag.includes(tag)">
<SubListItem
:sub="element"
type="sub"
Expand All @@ -129,7 +134,7 @@
<div v-if="hasCollections">
<div class="sticky-title-wrappers">
<p class="list-title" @click="toggleColFold">
<p>{{ $t(`specificWord.collectionSub`) }}</p>
<p>{{ $t(`specificWord.collectionSub`) + '('+collections.length+')'}}</p>
<nut-icon v-if="!isColFold" name="rect-down" size="12px"></nut-icon>
<nut-icon v-else name="rect-right" size="12px"></nut-icon>
</p>
Expand All @@ -155,7 +160,7 @@
@end="handleDragEnd(collections)"
>
<template #item="{ element }">
<div :key="element.name" class="draggable-item">
<div :key="element.name" class="draggable-item" v-show="tag === 'all' || element.tag.includes(tag)">
<SubListItem
:collection="element"
type="collection"
Expand Down Expand Up @@ -216,7 +221,7 @@

<script lang="ts" setup>
import { storeToRefs } from "pinia";
import { ref, toRaw } from "vue";
import { ref, toRaw, computed } from "vue";
import draggable from "vuedraggable";
import { useAppNotifyStore } from "@/store/appNotify";
Expand All @@ -235,6 +240,7 @@ const { env } = useBackend();
const { showNotify } = useAppNotifyStore();
const subsApi = useSubsApi();
const { t } = useI18n();
const addSubBtnIsVisible = ref(false);
const isSubFold = ref(localStorage.getItem('sub-fold') === '1');
const isColFold = ref(localStorage.getItem('col-fold') === '1');
Expand All @@ -252,6 +258,34 @@ const swipeDisabled = ref(false);
const touchStartY = ref(null);
const touchStartX = ref(null);
const sortFailed = ref(false);
const tags = computed(() => {
if(!hasSubs.value && !hasCollections.value) return []
// 从 subs 和 collections 中获取所有的 tag, 去重
const set = new Set()
subs.value.forEach(sub => {
if (Array.isArray(sub.tag) && sub.tag.length > 0) {
sub.tag.forEach(i => {
set.add(i)
});
}
})
collections.value.forEach(col => {
if (Array.isArray(col.tag) && col.tag.length > 0) {
col.tag.forEach(i => {
set.add(i)
});
}
})
let tags: any[] = Array.from(set)
if(tags.length === 0) return []
tags = tags.map(i => ({ label: i, value: i }));
return [{ label: t("specificWord.all"), value: "all" }, ...tags]
});
const tag = ref('all');
const onTouchStart = (event: TouchEvent) => {
touchStartY.value = Math.abs(event.touches[0].clientY);
touchStartX.value = Math.abs(event.touches[0].clientX);
Expand Down Expand Up @@ -362,7 +396,7 @@ const toggleColFold = () => {
};
</script>

<style lang="scss">
<style lang="scss" scoped>
.drag-btn-wrapper {
position: relative;
z-index: 999;
Expand Down Expand Up @@ -531,5 +565,16 @@ const toggleColFold = () => {
width: calc(100% - 1.5rem);
margin-left: auto;
margin-right: auto;
.radio-wrapper {
margin: 15px 0 0 0;
display: flex;
// justify-content: end;
:deep(.nut-radio__button.false) {
background: var(--divider-color);
border-color: transparent;
color: var(--second-text-color);
}
}
}
</style>
32 changes: 30 additions & 2 deletions src/views/SubEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,20 @@
type="text"
/>
</nut-form-item>
<!-- tag -->
<nut-form-item
:label="$t(`editorPage.subConfig.basic.tag.label`)"
prop="tag"
>
<input
class="nut-input-text"
v-model.trim="form.tag"
:placeholder="
$t(`editorPage.subConfig.basic.tag.placeholder`)
"
type="text"
/>
</nut-form-item>
<!-- icon -->
<nut-form-item
:label="$t(`editorPage.subConfig.basic.icon.label`)"
Expand Down Expand Up @@ -215,7 +229,10 @@
:url="item[2]"
bg-color=""
></nut-avatar>
<span>&nbsp;{{ item[1] }}</span>
<span class="sub-item">
&nbsp;{{ item[1] }}
<span class="tag" v-for="i in item[3]" :key="i"><nut-tag>{{ i }}</nut-tag></span>
</span>
</div>
</nut-checkbox>
</nut-checkboxgroup>
Expand Down Expand Up @@ -327,6 +344,7 @@
item.name,
item.displayName || item['display-name'] || item.name,
item.icon || (isDefaultIcon.value ? logoIcon : logoRedIcon),
item.tag
];
});
});
Expand All @@ -344,6 +362,7 @@
const form = reactive<any>({
name: '',
displayName: '',
form: '',
mergeSources: '',
ignoreFailedRemoteSub: false,
icon: '',
Expand Down Expand Up @@ -388,6 +407,7 @@
form.process = newProcess;
form.subUserinfo = sourceData.subUserinfo;
form.proxy = sourceData.proxy;
form.tag = Array.isArray(sourceData.tag) ? sourceData.tag.join(', ') : sourceData.tag;
switch (editType) {
case 'collections':
Expand Down Expand Up @@ -554,6 +574,7 @@
Toast.loading('拉取订阅中...', { id: 'submits', cover: true, duration: 1500 });
// 如果验证成功,开始保存/修改
const data: any = JSON.parse(JSON.stringify(toRaw(form)));
data.tag = [...new Set((data.tag||'').split(',').map((item: string) => item.trim()).filter((item: string) => item.length))];
data['display-name'] = data.displayName;
data.process = actionsToProcess(data.process, actionsList, ignoreList);
Expand Down Expand Up @@ -606,7 +627,7 @@
if (/\//.test(val)) {
resolve(false)
}
const nameList = subsStore.subs.map(item => item.name);
const nameList = [...subsStore.subs.map(item => item.name), ...subsStore.collections.map(item => item.name)];
nameList.includes(val) && configName !== val
? resolve(false)
: resolve(true);
Expand Down Expand Up @@ -782,6 +803,13 @@
overflow: hidden;
}
.sub-item {
display: flex;
.tag {
margin: 0 2px
}
}
.sub-item-customer-icon {
margin-right: 12px;
Expand Down

0 comments on commit 0e1066c

Please sign in to comment.