瀏覽代碼

promotion cate

bianjiang 1 年之前
父節點
當前提交
a114def8cf

+ 286 - 0
src/pages/website/Promotion2/components/CategoryFilter.tsx

@@ -0,0 +1,286 @@
+import { useResource } from "@/modules/resource";
+import { Button, Checkbox, Tooltip } from "ant-design-vue";
+import { CaretDownOutlined, RightOutlined } from "@ant-design/icons-vue";
+import { defineUI } from "queenjs";
+import { onMounted, reactive, defineComponent } from "vue";
+import { string, any } from "vue-types";
+import { css } from "@linaria/core";
+
+export default defineUI({
+  props: {
+    sourceType: string(),
+  },
+  emits: ["add", "change"],
+  setup(props, { emit }) {
+    const { controls } = useResource();
+    const state = reactive({
+      categoriesVal: {} as any,
+    });
+    const getCate = () => {
+      const categories = controls.categoryCtrl.state.categories;
+      const currCate: any = categories.find((e: any) => {
+        return e.value == props.sourceType;
+      });
+      return currCate?.children || [];
+    };
+
+    onMounted(() => {
+      getCate();
+    });
+    const itemChange = (v: { [key: string]: string }) => {
+      const values = { ...state.categoriesVal, ...v };
+      state.categoriesVal = values;
+      Object.keys(values).map((key: any) => {
+        if (values[key] == undefined) {
+          delete values[key];
+        }
+      });
+      const categories = Object.values(values);
+      emit(
+        "change",
+        categories && categories.length > 0 ? categories : undefined
+      );
+    };
+    return () => {
+      const categoryList = getCate();
+      return (
+        <div class="flex mt-20px items-center">
+          <div class="flex-1 flex pr-20px">
+            {categoryList.map((item: any) => {
+              if (!item.children) return null;
+              return <FilterItem data={item} onChange={itemChange} />;
+            })}
+          </div>
+          <Button type="primary" onClick={() => emit("add")}>
+            新增+
+          </Button>
+        </div>
+      );
+    };
+  },
+});
+const FilterItem = defineComponent({
+  props: {
+    data: any(),
+  },
+  emits: ["change"],
+  setup(props, { emit }) {
+    const state = reactive({
+      overlayVisible: false,
+      selected: [] as any,
+      indeterminate: [] as any,
+      categoryMap: new Map<string, any>(),
+      value: "" as any,
+    });
+    const initCateMap = (options: any, parent = "") => {
+      options.map((e: any) => {
+        if (e.children) {
+          state.categoryMap.set(e.id, {
+            parent: parent,
+            children: e.children,
+          });
+          initCateMap(e.children, e.id);
+        }
+      });
+    };
+    initCateMap([props.data]);
+    const selectItem = (id: string | null, index: number) => {
+      let selected = [...state.selected];
+      if (index > selected.length - 1) {
+        selected.push(id);
+      } else {
+        if (selected[index + 1]) {
+          selected.splice(index + 1, selected.length - 1);
+        }
+        selected.splice(index, 1, id);
+      }
+
+      state.selected = selected;
+    };
+    const changeSelectValue = (e: any, id: string, parent: string) => {
+      const checked = e.target?.checked;
+      if (checked) {
+        state.value = id;
+      } else {
+        state.value = undefined;
+      }
+      const value: any = {};
+      value[props.data.id] = state.value;
+      emit("change", value);
+      setIndeterminateVal(checked, parent, id);
+    };
+    const setIndeterminateVal = (add: boolean, parent: string, id: string) => {
+      if (add) {
+        const sameLevel = state.categoryMap.get(parent);
+        if (sameLevel && sameLevel.children) {
+          sameLevel.children.map((e: any) => {
+            const index = state.indeterminate.indexOf(e.id);
+            if (index != -1) {
+              state.indeterminate.splice(index, 1);
+            }
+          });
+        }
+        const index = state.indeterminate.indexOf(id);
+        if (index != -1) {
+          state.indeterminate.splice(index, 1);
+          return;
+        }
+        if (state.indeterminate.includes(parent)) {
+          return;
+        }
+        state.indeterminate.push(parent);
+      } else {
+        const index = state.indeterminate.indexOf(parent);
+        if (index == -1) {
+          return;
+        }
+        state.indeterminate.splice(index, 1);
+      }
+    };
+    function renderOverlay() {
+      const { data } = props;
+      return (
+        <div class={OverlayStyle}>
+          <div class={"cate_wapper"}>
+            <div class={"wapper_item"}>
+              {data.children.map((category: any) => {
+                return (
+                  <div
+                    class="cate_item"
+                    onMouseenter={() => {
+                      selectItem(category.id, 0);
+                    }}
+                  >
+                    <Checkbox
+                      checked={state.value == category.id}
+                      indeterminate={state.indeterminate.includes(category.id)}
+                      onChange={(e) => {
+                        changeSelectValue(e, category.id, data.id);
+                      }}
+                    >
+                      {category.name}
+                      {category.children && category.children.length > 0 && (
+                        <RightOutlined class={"arrow"} />
+                      )}
+                    </Checkbox>
+                  </div>
+                );
+              })}
+            </div>
+
+            {state.selected.map((select: string, index: number) => {
+              const categories = state.categoryMap.get(select);
+              return categories ? (
+                <div class={"wapper_item"}>
+                  {categories.children.map((item: any) => {
+                    return (
+                      <div
+                        class="cate_item"
+                        onMouseenter={() => {
+                          selectItem(item.id, index + 1);
+                        }}
+                      >
+                        <Checkbox
+                          checked={state.value == item.id}
+                          indeterminate={state.indeterminate.includes(item.id)}
+                          onChange={(e) => {
+                            changeSelectValue(e, item.id, select);
+                          }}
+                        >
+                          {item.name}
+                          {item.children && item.children.length > 0 && (
+                            <RightOutlined class={"arrow"} />
+                          )}
+                        </Checkbox>
+                      </div>
+                    );
+                  })}
+                </div>
+              ) : null;
+            })}
+          </div>
+        </div>
+      );
+    }
+    return () => {
+      const { data } = props;
+      return (
+        <div class={FilterItemRoot}>
+          <Tooltip
+            overlayClassName={OverlayClass}
+            destroyTooltipOnHide={true}
+            placement="bottomLeft"
+            trigger={"click"}
+            title={renderOverlay()}
+          >
+            <div class={"filter_btn"}>
+              <span class={"mr-6px"}>{data.name}</span>
+              <CaretDownOutlined style={{ fontSize: "12px" }} />
+            </div>
+          </Tooltip>
+        </div>
+      );
+    };
+  },
+});
+const FilterItemRoot = css`
+  margin-right: 20px;
+  .filter_btn {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding-bottom: 10px;
+    font-size: 16px;
+    line-height: 24px;
+    cursor: pointer;
+    color: #fff;
+    border-bottom: 2px solid transparent;
+    &:hover {
+      border-color: @inf-primary-color;
+    }
+  }
+`;
+const OverlayClass = css`
+  .ant-tooltip-arrow {
+    display: none;
+  }
+  .ant-tooltip-inner {
+    padding: 0;
+    background-color: #303030;
+  }
+`;
+const OverlayStyle = css`
+  .cate_wapper {
+    display: flex;
+    flex-wrap: nowrap;
+    .wapper_item {
+      flex: auto;
+      width: 120px;
+      overflow: auto;
+      border-left: 1px solid #1f1f1f;
+      &:first-child {
+        border-left: none;
+      }
+      .cate_item {
+        .ant-checkbox-wrapper,
+        .cate_text_item {
+          position: relative;
+          width: 100%;
+          padding: 10px 14px;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          &:hover {
+            background-color: #404040;
+          }
+          .arrow {
+            position: absolute;
+            right: 10px;
+            top: 50%;
+            transform: translateY(-50%);
+          }
+        }
+      }
+    }
+  }
+`;

+ 1 - 7
src/pages/website/Promotion2/components/Header.tsx

@@ -1,17 +1,11 @@
-
-import { Button } from "ant-design-vue";
 import { defineUI } from "queenjs";
 
 export default defineUI({
   emits: ["add"],
-  setup(props, {emit}) {
-    
+  setup(props, { emit }) {
     return () => (
       <div class="flex items-center justify-between">
         <h3 class="text-22px">我的作品</h3>
-        <Button type="primary" onClick={()=>emit("add")}>
-          新增+
-        </Button>
       </div>
     );
   },

+ 16 - 0
src/pages/website/Promotion2/components/index.tsx

@@ -4,6 +4,7 @@ import { defineUI } from "queenjs";
 import { onMounted } from "vue";
 import { any } from "vue-types";
 import Header from "./Header";
+import Filter from "./CategoryFilter";
 import PromotionItem from "./PromotionItem";
 import { useAuth } from "@queenjs-modules/auth";
 
@@ -14,12 +15,22 @@ export default defineUI({
 
   slots: {
     Header,
+    Filter,
     List,
   },
   setup(props, { slots }) {
     const auth = useAuth();
 
+    const FilterChange = (query: any) => {
+      const ctrlQuery = props.Controller.ListCtrl.state.query || {};
+      props.Controller.ListCtrl.state.query = {
+        ...ctrlQuery,
+        categories: query,
+      };
+      props.Controller.ListCtrl.loadPage(1);
+    };
     onMounted(() => {
+      props.Controller.ListCtrl.state.query = {};
       props.Controller.ListCtrl.loadPage(1);
     });
 
@@ -29,6 +40,11 @@ export default defineUI({
       return (
         <div>
           <slots.Header onAdd={props.Controller.createPromotion} />
+          <slots.Filter
+            sourceType="template"
+            onAdd={props.Controller.createPromotion}
+            onChange={FilterChange}
+          />
           <slots.List
             gap="25px"
             class="my-30px"

+ 2 - 0
src/styles/global.less

@@ -8,6 +8,8 @@ html,
 body {
   color: @inf-text-color;
   background-color: @inf-layout-bg;
+  height: 100%;
+  overflow: hidden;
 }
 
 #app {