Просмотр исходного кода

update vue3.3
web编辑 修正筛选删除导出逻辑

Xwite 3 лет назад
Родитель
Сommit
1b6c8f5b2f

+ 0 - 1
modules/web/.eslintrc-auto-import.json

@@ -4,7 +4,6 @@
     "ComponentPublicInstance": true,
     "ComputedRef": true,
     "EffectScope": true,
-    "ElLoading": true,
     "ElMessage": true,
     "InjectionKey": true,
     "PropType": true,

+ 12 - 12
modules/web/package.json

@@ -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"
   }
 }

+ 0 - 1
modules/web/src/components/BookItems.vue

@@ -64,7 +64,6 @@ const getCover = (coverUrl) => {
 const subJustify = computed(() =>
   props.isSearch ? "space-between" : "flex-start"
 );
-
 </script>
 
 <style lang="scss" scoped>

+ 6 - 1
modules/web/src/components/CatalogItem.vue

@@ -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;

+ 6 - 3
modules/web/src/components/SourceDebug.vue

@@ -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");

+ 3 - 2
modules/web/src/components/SourceItem.vue

@@ -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);
 };

+ 35 - 16
modules/web/src/components/SourceList.vue

@@ -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 = () => {

+ 4 - 4
modules/web/src/components/SourceTabTools.vue

@@ -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>

+ 11 - 18
modules/web/src/store/sourceStore.js

@@ -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));
     },

+ 49 - 38
modules/web/src/utils/souce.ts

@@ -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 = {}

+ 1 - 1
modules/web/src/views/BookChapter.vue

@@ -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);

+ 3 - 3
modules/web/src/views/SourceEditor.vue

@@ -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>