mirror of
https://github.com/gedoor/legado.git
synced 2024-07-06 23:47:49 +08:00
update vue3.3
web编辑 修正筛选删除导出逻辑
This commit is contained in:
parent
152dc4e3b8
commit
5efc367bc6
@ -4,7 +4,6 @@
|
||||
"ComponentPublicInstance": true,
|
||||
"ComputedRef": true,
|
||||
"EffectScope": true,
|
||||
"ElLoading": true,
|
||||
"ElMessage": true,
|
||||
"InjectionKey": true,
|
||||
"PropType": true,
|
||||
|
@ -15,26 +15,26 @@
|
||||
"dependencies": {
|
||||
"@element-plus/icons-svg": "^2.1.0",
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"@vueuse/shared": "^10.0.2",
|
||||
"axios": "^1.3.5",
|
||||
"element-plus": "^2.3.3",
|
||||
"@vueuse/shared": "^10.1.2",
|
||||
"axios": "^1.4.0",
|
||||
"element-plus": "^2.3.4",
|
||||
"hotkeys-js": "^3.10.2",
|
||||
"pinia": "^2.0.34",
|
||||
"vue": "^3.2.47",
|
||||
"vue-router": "^4.1.6",
|
||||
"pinia": "^2.0.36",
|
||||
"vue": "^3.3.2",
|
||||
"vue-router": "^4.2.0",
|
||||
"vue3-virtual-scroll-list": "^0.2.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^4.1.0",
|
||||
"eslint": "^8.38.0",
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"eslint": "^8.40.0",
|
||||
"eslint-config-prettier": "^8.8.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-vue": "^9.11.0",
|
||||
"prettier": "^2.8.7",
|
||||
"sass": "^1.62.0",
|
||||
"eslint-plugin-vue": "^9.12.0",
|
||||
"prettier": "^2.8.8",
|
||||
"sass": "^1.62.1",
|
||||
"unplugin-auto-import": "^0.15.3",
|
||||
"unplugin-icons": "^0.16.1",
|
||||
"unplugin-vue-components": "^0.24.1",
|
||||
"vite": "^4.2.1"
|
||||
"vite": "^4.3.5"
|
||||
}
|
||||
}
|
||||
|
@ -64,7 +64,6 @@ const getCover = (coverUrl) => {
|
||||
const subJustify = computed(() =>
|
||||
props.isSearch ? "space-between" : "flex-start"
|
||||
);
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
@ -12,7 +12,12 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
const props = defineProps(["index", "source", "gotoChapter", "currentChapterIndex"]);
|
||||
const props = defineProps([
|
||||
"index",
|
||||
"source",
|
||||
"gotoChapter",
|
||||
"currentChapterIndex",
|
||||
]);
|
||||
|
||||
const isSelected = (idx) => {
|
||||
return idx == props.currentChapterIndex;
|
||||
|
@ -27,9 +27,12 @@ const store = useSourceStore();
|
||||
const printDebug = ref("");
|
||||
const searchKey = ref("");
|
||||
|
||||
watch(() => store.isDebuging, () => {
|
||||
if (store.isDebuging) startDebug();
|
||||
});
|
||||
watch(
|
||||
() => store.isDebuging,
|
||||
() => {
|
||||
if (store.isDebuging) startDebug();
|
||||
}
|
||||
);
|
||||
|
||||
const appendDebugMsg = (msg) => {
|
||||
let debugDom = document.querySelector("#debug-text");
|
||||
|
@ -15,11 +15,12 @@
|
||||
|
||||
<script setup>
|
||||
import { Edit } from "@element-plus/icons-vue";
|
||||
import { getSourceUniqueKey } from "@/utils/souce";
|
||||
|
||||
const props = defineProps(["source"]);
|
||||
const store = useSourceStore();
|
||||
const { savedSourcesMap, currentSourceUrl, sourceUrlKey } = storeToRefs(store);
|
||||
const sourceUrl = computed(() => props.source[sourceUrlKey.value]);
|
||||
const { savedSourcesMap, currentSourceUrl } = storeToRefs(store);
|
||||
const sourceUrl = computed(() => getSourceUniqueKey(props.source));
|
||||
const handleSourceClick = (source) => {
|
||||
store.changeCurrentSource(source);
|
||||
};
|
||||
|
@ -18,7 +18,7 @@
|
||||
type="danger"
|
||||
:icon="Delete"
|
||||
@click="deleteSelectSources"
|
||||
:disabled="sourceUrlSelect.length === 0"
|
||||
:disabled="sourceSelect.length === 0"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button
|
||||
@ -43,7 +43,11 @@
|
||||
<script setup>
|
||||
import API from "@api";
|
||||
import { Folder, Delete, Download, Search } from "@element-plus/icons-vue";
|
||||
import { isSourceContains } from "@utils/souce";
|
||||
import {
|
||||
isSourceMatches,
|
||||
getSourceUniqueKey,
|
||||
convertSourcesToMap,
|
||||
} from "@utils/souce";
|
||||
import VirtualList from "vue3-virtual-scroll-list";
|
||||
import SourceItem from "./SourceItem.vue";
|
||||
|
||||
@ -52,35 +56,50 @@ const sourceUrlSelect = ref([]);
|
||||
const searchKey = ref("");
|
||||
const { sources, sourcesMap } = storeToRefs(store);
|
||||
|
||||
// 筛选源
|
||||
/** @type Ref<import('@/source').Source[]> */
|
||||
const sourcesFiltered = computed(() => {
|
||||
const key = searchKey.value;
|
||||
if (key === "") return sources.value;
|
||||
return (
|
||||
sources.value
|
||||
// @ts-ignore
|
||||
.filter((source) => isSourceMatches(source, key))
|
||||
);
|
||||
});
|
||||
// 计算当前筛选关键词下的选中源
|
||||
/** @type Ref<import('@/source').Source[]> */
|
||||
const sourceSelect = computed(() => {
|
||||
const urls = sourceUrlSelect.value;
|
||||
if (urls.length == 0) return [];
|
||||
return urls.map(
|
||||
(sourceUrl) => sourcesMap.value.get(sourceUrl) ?? {}
|
||||
);
|
||||
const sourcesFilteredMap =
|
||||
searchKey.value == ""
|
||||
? sourcesMap.value
|
||||
: convertSourcesToMap(sourcesFiltered.value);
|
||||
return urls.reduce((sources, sourceUrl) => {
|
||||
const source = sourcesFilteredMap.get(sourceUrl);
|
||||
if (source) sources.push(source);
|
||||
return sources;
|
||||
}, []);
|
||||
});
|
||||
|
||||
const deleteSelectSources = () => {
|
||||
const sourceSelectValue = sourceSelect.value;
|
||||
API.deleteSource(sourceSelectValue).then(({ data }) => {
|
||||
if (!data.isSuccess) return ElMessage.error(data.errorMsg);
|
||||
store.deleteSources(sourceSelectValue);
|
||||
sourceUrlSelect.value = [];
|
||||
const sourceUrlSelectRawValue = toRaw(sourceUrlSelect.value);
|
||||
sourceSelectValue.forEach((source) => {
|
||||
const index = sourceUrlSelectRawValue.indexOf(getSourceUniqueKey(source));
|
||||
if (index > -1) sourceUrlSelectRawValue.splice(index, 1);
|
||||
});
|
||||
sourceUrlSelect.value = sourceUrlSelectRawValue;
|
||||
});
|
||||
};
|
||||
const clearAllSources = () => {
|
||||
store.clearAllSource();
|
||||
sourceUrlSelect.value = [];
|
||||
};
|
||||
//筛选源
|
||||
const sourcesFiltered = computed(() => {
|
||||
let key = searchKey.value;
|
||||
if (key === "") return sources.value;
|
||||
return (
|
||||
sources.value
|
||||
// @ts-ignore
|
||||
.filter((source) => isSourceContains(source, key))
|
||||
);
|
||||
});
|
||||
|
||||
//导入本地文件
|
||||
const importSourceFile = () => {
|
||||
|
@ -6,10 +6,10 @@
|
||||
:name="tab[0]"
|
||||
:label="tab[1]"
|
||||
>
|
||||
<source-json v-show="index == 0" />
|
||||
<source-debug v-show="index == 1" />
|
||||
<source-list v-show="index == 2" />
|
||||
<source-help v-show="index == 3" />
|
||||
<source-json v-if="index == 0" />
|
||||
<source-debug v-if="index == 1" />
|
||||
<source-list v-if="index == 2" />
|
||||
<source-help v-if="index == 3" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
@ -1,5 +1,10 @@
|
||||
import { defineStore } from "pinia";
|
||||
import { emptyBookSource, emptyRssSource } from "@utils/souce";
|
||||
import {
|
||||
emptyBookSource,
|
||||
emptyRssSource,
|
||||
getSourceUniqueKey,
|
||||
convertSourcesToMap,
|
||||
} from "@utils/souce";
|
||||
|
||||
const isBookSource = /bookSource/i.test(location.href);
|
||||
const emptySource = isBookSource ? emptyBookSource : emptyRssSource;
|
||||
@ -22,21 +27,9 @@ export const useSourceStore = defineStore("source", {
|
||||
},
|
||||
getters: {
|
||||
sources: (state) => (isBookSource ? state.bookSources : state.rssSources),
|
||||
sourceUrlKey: () => (isBookSource ? "bookSourceUrl" : "sourceUrl"),
|
||||
sourcesMap: (state) => {
|
||||
let map = new Map();
|
||||
state.sources.forEach((source) =>
|
||||
map.set(source[state.sourceUrlKey], source)
|
||||
);
|
||||
return map;
|
||||
},
|
||||
savedSourcesMap: (state) => {
|
||||
let map = new Map();
|
||||
state.savedSources.forEach((source) =>
|
||||
map.set(source[state.sourceUrlKey], source)
|
||||
);
|
||||
return map;
|
||||
},
|
||||
// @ts-ignore
|
||||
sourcesMap: (state) => convertSourcesToMap(state.sources),
|
||||
savedSourcesMap: (state) => convertSourcesToMap(state.savedSources),
|
||||
currentSourceUrl: (state) =>
|
||||
isBookSource
|
||||
? state.currentSource.bookSourceUrl
|
||||
@ -79,10 +72,10 @@ export const useSourceStore = defineStore("source", {
|
||||
saveCurrentSource() {
|
||||
let source = this.currentSource,
|
||||
map = this.sourcesMap;
|
||||
map.set(source[this.sourceUrlKey], source);
|
||||
map.set(getSourceUniqueKey(source), Object.create(source));
|
||||
this.saveSources(Array.from(map.values()));
|
||||
},
|
||||
// 更改当前编辑的源
|
||||
// 更改当前编辑的源qq
|
||||
changeCurrentSource(source) {
|
||||
this.currentSource = JSON.parse(JSON.stringify(source));
|
||||
},
|
||||
|
@ -1,38 +1,49 @@
|
||||
import { Source } from '../source'
|
||||
|
||||
|
||||
const isNullOrBlank = (string: string | null | undefined | number) => string == null || (string as string).length === 0 || /^\s+$/.test(string as string)
|
||||
const isBookSource = (source: Source) => "bookSourceName" in source
|
||||
|
||||
export const isInvaildSource: (source: Source) => boolean = (source) => {
|
||||
if (isBookSource(source)) {
|
||||
return !isNullOrBlank(source.bookSourceName) &&
|
||||
!isNullOrBlank(source.bookSourceUrl) &&
|
||||
!isNullOrBlank(source.bookSourceType)
|
||||
}
|
||||
return !isNullOrBlank(source.sourceName) &&
|
||||
!isNullOrBlank(source.sourceName)
|
||||
}
|
||||
|
||||
export const isSourceContains: (source: Source, searchKey: string) => boolean = (source, searchKey) => {
|
||||
if (isBookSource(source)) {
|
||||
return (source.bookSourceName?.includes(searchKey) ||
|
||||
source.bookSourceUrl?.includes(searchKey) ||
|
||||
source.bookSourceGroup?.includes(searchKey) ||
|
||||
source.bookSourceComment?.includes(searchKey)) ?? false
|
||||
}
|
||||
return (source.sourceName?.includes(searchKey) ||
|
||||
source.sourceUrl?.includes(searchKey) ||
|
||||
source.sourceGroup?.includes(searchKey) ||
|
||||
source.sourceComment?.includes(searchKey)) ?? false
|
||||
}
|
||||
|
||||
export const emptyBookSource = {
|
||||
ruleSearch: {},
|
||||
ruleBookInfo: {},
|
||||
ruleToc: {},
|
||||
ruleContent: {},
|
||||
ruleReview: {},
|
||||
ruleExplore: {}
|
||||
}
|
||||
export const emptyRssSource = {}
|
||||
import { Source } from '../source'
|
||||
|
||||
|
||||
const isNullOrBlank = (string: string | null | undefined | number) => string == null || (string as string).length === 0 || /^\s+$/.test(string as string)
|
||||
const isBookSource = (source: Source) => "bookSourceName" in source
|
||||
|
||||
export const isInvaildSource: (source: Source) => boolean = (source) => {
|
||||
if (isBookSource(source)) {
|
||||
return !isNullOrBlank(source.bookSourceName) &&
|
||||
!isNullOrBlank(source.bookSourceUrl) &&
|
||||
!isNullOrBlank(source.bookSourceType)
|
||||
}
|
||||
return !isNullOrBlank(source.sourceName) &&
|
||||
!isNullOrBlank(source.sourceName)
|
||||
}
|
||||
|
||||
export const getSourceUniqueKey = (source: Source) => isBookSource(source) ? source.bookSourceUrl : source.sourceUrl;
|
||||
|
||||
export const isSourceMatches: (source: Source, searchKey: string) => boolean = (source, searchKey) => {
|
||||
// TODO: 正则和普通字符串识别 识别 * . \ [ ] <= <! != = ?: () \d\w\s\...
|
||||
if (isBookSource(source)) {
|
||||
return (source.bookSourceName?.includes(searchKey) ||
|
||||
source.bookSourceUrl?.includes(searchKey) ||
|
||||
source.bookSourceGroup?.includes(searchKey) ||
|
||||
source.bookSourceComment?.includes(searchKey)) ?? false
|
||||
}
|
||||
return (source.sourceName?.includes(searchKey) ||
|
||||
source.sourceUrl?.includes(searchKey) ||
|
||||
source.sourceGroup?.includes(searchKey) ||
|
||||
source.sourceComment?.includes(searchKey)) ?? false
|
||||
}
|
||||
|
||||
export const convertSourcesToMap = (sources: Source[]): Map<string, Source> => {
|
||||
const map = new Map();
|
||||
sources.forEach((source) =>
|
||||
map.set(getSourceUniqueKey(source), source)
|
||||
);
|
||||
return map;
|
||||
}
|
||||
|
||||
export const emptyBookSource = {
|
||||
ruleSearch: {},
|
||||
ruleBookInfo: {},
|
||||
ruleToc: {},
|
||||
ruleContent: {},
|
||||
ruleReview: {},
|
||||
ruleExplore: {}
|
||||
}
|
||||
export const emptyRssSource = {}
|
||||
|
@ -318,7 +318,7 @@ const saveReadingBookProgressToBrowser = (index, pos) => {
|
||||
* VisibilityChange https://developer.mozilla.org/zh-CN/docs/Web/API/Document/visibilitychange_event
|
||||
* 监听关闭页面 切换tab 返回桌面 等操作
|
||||
* 注意不用监听点击链接导航变化 不对Safari<14.5兼容处理
|
||||
**/
|
||||
**/
|
||||
const onVisibilityChange = () => {
|
||||
if (document.visibilityState == "hidden") {
|
||||
API.saveBookProgressWithBeacon(bookProgress.value);
|
||||
|
@ -10,13 +10,13 @@ import bookSourceConfig from "@/utils/bookSourceEditConfig.js";
|
||||
import rssSourceConfig from "@/utils/rssSourceEditConfig.js";
|
||||
import "@/assets/main.css";
|
||||
|
||||
const config = ref({});
|
||||
let config;
|
||||
|
||||
if (/bookSource/i.test(location.href)) {
|
||||
config.value = bookSourceConfig;
|
||||
config = bookSourceConfig;
|
||||
document.title = "书源管理";
|
||||
} else {
|
||||
config.value = rssSourceConfig;
|
||||
config = rssSourceConfig;
|
||||
document.title = "订阅源管理";
|
||||
}
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user