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

Merge branch 'flow' of ruiqigogs/yf-portal-vue into master

yanghao 3 недель назад
Родитель
Сommit
d9d0522ca4
1 измененных файлов с 218 добавлено и 9 удалено
  1. 218 9
      src/views/flow/index.vue

+ 218 - 9
src/views/flow/index.vue

@@ -65,6 +65,21 @@
     </section>
     </section>
 
 
     <div class="content">
     <div class="content">
+      <div class="search-bar">
+        <div class="search-input">
+          <Icon icon="mdi:magnify" class="search-icon" />
+          <input
+            v-model.trim="searchKeyword"
+            type="text"
+            class="search-field"
+            placeholder="搜索流程名称"
+          />
+        </div>
+        <span v-if="searchKeyword" class="search-meta">
+          共找到 {{ displayedFlows.length }} 条结果
+        </span>
+      </div>
+
       <div class="tabs-container" role="tablist" aria-label="EHR模块">
       <div class="tabs-container" role="tablist" aria-label="EHR模块">
         <button
         <button
           class="el-tab-item"
           class="el-tab-item"
@@ -91,16 +106,27 @@
       </div>
       </div>
 
 
       <div role="tabpanel">
       <div role="tabpanel">
-        <div class="items-grid">
+        <div v-if="displayedFlows.length" class="items-grid">
           <div
           <div
-            v-for="item in activeTab.flowRespVOS"
+            v-for="item in displayedFlows"
             :key="item.id"
             :key="item.id"
             class="item-card"
             class="item-card"
+            :style="{
+              '--item-accent': getIconTheme(item).color,
+              '--item-accent-soft': getIconTheme(item).softColor,
+              '--item-accent-hover': getIconTheme(item).hoverColor,
+            }"
             @click="go(item)"
             @click="go(item)"
           >
           >
             <div class="item-top">
             <div class="item-top">
-              <div class="item-icon">
-                <Icon :icon="item.icon || 'mdi:file-document-outline'" />
+              <div
+                class="item-icon"
+                :style="{ backgroundColor: getIconTheme(item).softColor }"
+              >
+                <Icon
+                  :icon="item.icon || 'mdi:file-document-outline'"
+                  :color="getIconTheme(item).color"
+                />
               </div>
               </div>
 
 
               <div>
               <div>
@@ -146,6 +172,11 @@
             </div> -->
             </div> -->
           </div>
           </div>
         </div>
         </div>
+        <div v-else class="empty-state">
+          <Icon icon="mdi:file-search-outline" class="empty-icon" />
+          <p class="empty-title">没有找到相关流程</p>
+          <p class="empty-desc">试试换个名称关键词搜索</p>
+        </div>
       </div>
       </div>
     </div>
     </div>
 
 
@@ -350,6 +381,61 @@ const handleResize = () => {
 const tabs = ref([]);
 const tabs = ref([]);
 
 
 const activeKey = ref("all");
 const activeKey = ref("all");
+const searchKeyword = ref("");
+
+const iconPalette = [
+  {
+    color: "#2563eb",
+    softColor: "rgba(37, 99, 235, 0.12)",
+    hoverColor: "rgba(37, 99, 235, 0.18)",
+  },
+  {
+    color: "#db2777",
+    softColor: "rgba(219, 39, 119, 0.12)",
+    hoverColor: "rgba(219, 39, 119, 0.18)",
+  },
+  {
+    color: "#ea580c",
+    softColor: "rgba(234, 88, 12, 0.12)",
+    hoverColor: "rgba(234, 88, 12, 0.18)",
+  },
+  {
+    color: "#059669",
+    softColor: "rgba(5, 150, 105, 0.12)",
+    hoverColor: "rgba(5, 150, 105, 0.18)",
+  },
+  {
+    color: "#7c3aed",
+    softColor: "rgba(124, 58, 237, 0.12)",
+    hoverColor: "rgba(124, 58, 237, 0.18)",
+  },
+  {
+    color: "#0f766e",
+    softColor: "rgba(15, 118, 110, 0.12)",
+    hoverColor: "rgba(15, 118, 110, 0.18)",
+  },
+  {
+    color: "#dc2626",
+    softColor: "rgba(220, 38, 38, 0.12)",
+    hoverColor: "rgba(220, 38, 38, 0.18)",
+  },
+  {
+    color: "#ca8a04",
+    softColor: "rgba(202, 138, 4, 0.12)",
+    hoverColor: "rgba(202, 138, 4, 0.18)",
+  },
+];
+
+const hashText = (text = "") =>
+  String(text)
+    .split("")
+    .reduce((hash, char) => hash * 31 + char.charCodeAt(0), 0);
+
+const getIconTheme = (item) => {
+  const seed = item?.id ?? item?.flowName ?? item?.type ?? "";
+  const index = Math.abs(hashText(seed)) % iconPalette.length;
+  return iconPalette[index];
+};
 
 
 const allTab = computed(() => {
 const allTab = computed(() => {
   const flowRespVOS = tabs.value.flatMap((tab) => tab.flowRespVOS || []);
   const flowRespVOS = tabs.value.flatMap((tab) => tab.flowRespVOS || []);
@@ -370,6 +456,20 @@ const activeTab = computed(() => {
   );
   );
 });
 });
 
 
+const displayedFlows = computed(() => {
+  const keyword = searchKeyword.value.trim().toLowerCase();
+
+  if (!keyword) {
+    return activeTab.value.flowRespVOS || [];
+  }
+
+  return (allTab.value.flowRespVOS || []).filter((item) =>
+    String(item?.flowName || "")
+      .toLowerCase()
+      .includes(keyword)
+  );
+});
+
 const getAll = async () => {
 const getAll = async () => {
   const res = await getFlows({
   const res = await getFlows({
     pageNo: 1,
     pageNo: 1,
@@ -701,6 +801,60 @@ onBeforeUnmount(() => {
   /* height: 80vh; */
   /* height: 80vh; */
 }
 }
 
 
+.search-bar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 16px;
+  margin-bottom: 18px;
+}
+
+.search-input {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  width: min(420px, 100%);
+  padding: 0 16px;
+  height: 46px;
+  border-radius: 14px;
+  background: #ffffff;
+  border: 1px solid #e2e8f0;
+  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.04);
+  transition:
+    border-color 0.2s ease,
+    box-shadow 0.2s ease;
+}
+
+.search-input:focus-within {
+  border-color: rgba(2, 64, 155, 0.28);
+  box-shadow: 0 12px 28px rgba(2, 64, 155, 0.1);
+}
+
+.search-icon {
+  flex-shrink: 0;
+  font-size: 18px;
+  color: #94a3b8;
+}
+
+.search-field {
+  width: 100%;
+  border: none;
+  background: transparent;
+  outline: none;
+  font-size: 14px;
+  color: #0f172a;
+}
+
+.search-field::placeholder {
+  color: #94a3b8;
+}
+
+.search-meta {
+  flex-shrink: 0;
+  font-size: 13px;
+  color: #64748b;
+}
+
 .tabs-container {
 .tabs-container {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
@@ -801,6 +955,9 @@ onBeforeUnmount(() => {
 }
 }
 
 
 .item-card {
 .item-card {
+  --item-accent: #2563eb;
+  --item-accent-soft: rgba(37, 99, 235, 0.12);
+  --item-accent-hover: rgba(37, 99, 235, 0.18);
   display: flex;
   display: flex;
   flex-direction: column;
   flex-direction: column;
   gap: 18px;
   gap: 18px;
@@ -815,14 +972,14 @@ onBeforeUnmount(() => {
   transition:
   transition:
     transform 0.2s ease,
     transform 0.2s ease,
     border-color 0.2s ease,
     border-color 0.2s ease,
-    box-shadow 0.2s ease;
+    box-shadow 0.2s ease,
+    background-color 0.2s ease;
   cursor: pointer;
   cursor: pointer;
   border-bottom: 1px solid rgb(241, 245, 249);
   border-bottom: 1px solid rgb(241, 245, 249);
 }
 }
 
 
 .item-card:hover {
 .item-card:hover {
   transform: translateY(-3px);
   transform: translateY(-3px);
-
   box-shadow: 0 18px 36px rgba(15, 23, 42, 0.08);
   box-shadow: 0 18px 36px rgba(15, 23, 42, 0.08);
   color: #02409b !important;
   color: #02409b !important;
 }
 }
@@ -843,16 +1000,30 @@ onBeforeUnmount(() => {
   width: 52px;
   width: 52px;
   height: 52px;
   height: 52px;
   border-radius: 16px;
   border-radius: 16px;
-  background: #f5f7fb;
+
   display: grid;
   display: grid;
   place-items: center;
   place-items: center;
-
   font-size: 20px;
   font-size: 20px;
+  transition:
+    transform 0.2s ease,
+    background-color 0.2s ease,
+    box-shadow 0.2s ease;
 }
 }
 
 
 .item-icon :deep(svg) {
 .item-icon :deep(svg) {
   width: 24px;
   width: 24px;
   height: 24px;
   height: 24px;
+  transition: color 0.2s ease;
+}
+
+.item-card:hover .item-icon {
+  background: var(--item-accent) !important;
+  transform: translateY(-2px);
+  box-shadow: 0 10px 20px -12px var(--item-accent);
+}
+
+.item-card:hover .item-icon :deep(svg) {
+  color: #ffffff !important;
 }
 }
 
 
 .item-role {
 .item-role {
@@ -875,6 +1046,35 @@ onBeforeUnmount(() => {
   line-height: 1.6;
   line-height: 1.6;
 }
 }
 
 
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  gap: 10px;
+  min-height: 240px;
+  border-radius: 22px;
+  background: rgba(255, 255, 255, 0.72);
+  border: 1px dashed #dbe3ee;
+  color: #64748b;
+}
+
+.empty-icon {
+  font-size: 42px;
+  color: #94a3b8;
+}
+
+.empty-title {
+  font-size: 16px;
+  font-weight: 600;
+  color: #334155;
+}
+
+.empty-desc {
+  font-size: 13px;
+  color: #94a3b8;
+}
+
 .item-footer {
 .item-footer {
   display: flex;
   display: flex;
   align-items: center;
   align-items: center;
@@ -910,6 +1110,15 @@ onBeforeUnmount(() => {
     padding: 56px 7vw 36px;
     padding: 56px 7vw 36px;
   }
   }
 
 
+  .search-bar {
+    flex-direction: column;
+    align-items: stretch;
+  }
+
+  .search-input {
+    width: 100%;
+  }
+
   .panel-head {
   .panel-head {
     flex-direction: column;
     flex-direction: column;
     align-items: flex-start;
     align-items: flex-start;
@@ -1028,7 +1237,7 @@ onBeforeUnmount(() => {
   -webkit-backdrop-filter: blur(10px);
   -webkit-backdrop-filter: blur(10px);
   height: 200px !important;
   height: 200px !important;
   width: 100px !important;
   width: 100px !important;
-  overflow-y: auto !important;
+  overflow-y: hidden !important;
   box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1) !important;
   box-shadow: 0 4px 30px rgba(0, 0, 0, 0.1) !important;
   border-radius: 8px !important;
   border-radius: 8px !important;
   padding: 10px !important;
   padding: 10px !important;