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