bianjiang 1 year ago
parent
commit
c79ed50ada

+ 1 - 1
src/modules/editor/components/CompUI/basicUI/Text/component.tsx

@@ -258,7 +258,7 @@ const textStyle = css`
   font-size: 14px;
   width: 100%;
   color: #666;
-  word-break: break-all;
+  word-break: break-word;
   h2 {
     color: #111;
     font-size: 24px;

+ 1 - 1
src/modules/editor/components/CompUI/basicUI/Text/component2.tsx

@@ -219,7 +219,7 @@ const textStyle = css`
   font-size: 14px;
   width: 100%;
   color: #666;
-  word-break: break-all;
+  word-break: break-word;
   h2 {
     color: #111111;
     font-size: 24px;

+ 358 - 0
src/modules/editor/components/Viewport/Slider/SliderLeft/ListFilter/CateFilter.tsx

@@ -0,0 +1,358 @@
+import { css } from "@linaria/core";
+
+import { RightOutlined, DownOutlined } from "@ant-design/icons-vue";
+import { Button, Checkbox, Tooltip } from "ant-design-vue";
+import { defineComponent, reactive, watch } from "vue";
+import { any, string } from "vue-types";
+import { cloneDeep } from "lodash";
+
+export default defineComponent({
+  props: {
+    options: any().isRequired,
+    value: any().isRequired,
+  },
+  emits: ["change"],
+  setup(props, { emit }) {
+    const state = reactive({
+      overlayVisible: false,
+      selected: [] as any,
+      indeterminate: [] as any,
+      categoryMap: new Map<string, any>(),
+      value: {} as any,
+    });
+    function toggleFilter(visible: boolean) {
+      state.overlayVisible = visible;
+    }
+    watch(
+      () => props.options,
+      () => {
+        initCateMap(props.options);
+      }
+    );
+    watch(
+      () => props.value,
+      () => {
+        state.value = cloneDeep(props.value);
+        state.indeterminate = [];
+        initIndeterminate(props.value);
+      }
+    );
+    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.options);
+    const initIndeterminate = (value: any) => {
+      const values = cloneDeep(value);
+      Object.keys(values).map((e: string) => {
+        const item = state.categoryMap.get(e);
+        if (!item || !item.children) {
+          return;
+        }
+        initInChildren(item.parent, item.children, values[e]);
+      });
+    };
+    const initInChildren = (parent: string, children: any, id: string) => {
+      for (let i = 0; i < children.length; i++) {
+        if (children[i].id == id) {
+          setIndeterminateVal(true, parent, id);
+          break;
+        }
+        children[i].children &&
+          initInChildren(children[i].id, children[i].children, id);
+      }
+    };
+    const getRootParent = (id: string): string => {
+      const item = state.categoryMap.get(id);
+      if (!item.parent) {
+        return id;
+      } else {
+        return getRootParent(item.parent);
+      }
+    };
+    function reset() {
+      state.overlayVisible = false;
+      state.selected = [];
+      state.indeterminate = [];
+      state.value = {};
+      emit("change", undefined);
+    }
+
+    function submit() {
+      state.overlayVisible = false;
+      emit("change", state.value);
+    }
+    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;
+      const rootParent = getRootParent(parent);
+      if (checked) {
+        state.value[rootParent] = id;
+      } else {
+        state.value[rootParent] = undefined;
+      }
+      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() {
+      return (
+        <div class={OverlayStyle}>
+          <div class={"cate_wapper"}>
+            <div class={"wapper_item"}>
+              {props.options.map((category: any) => {
+                return (
+                  <div
+                    class="cate_item"
+                    onMouseenter={() => {
+                      selectItem(category.id, 0);
+                    }}
+                  >
+                    <div
+                      class={[
+                        "cate_text_item",
+                        state.value[category.id] ? "active" : null,
+                      ]}
+                    >
+                      {category.name}
+                    </div>
+                  </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) => {
+                    const rootParent = getRootParent(select);
+                    return (
+                      <div
+                        class="cate_item"
+                        onMouseenter={() => {
+                          selectItem(item.id, index + 1);
+                        }}
+                      >
+                        <Checkbox
+                          checked={state.value[rootParent] == 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 class="overlay_footer">
+            <Button class={"reset"} onClick={reset}>
+              重置
+            </Button>
+            <Button type="primary" onClick={submit}>
+              确定
+            </Button>
+          </div>
+        </div>
+      );
+    }
+
+    return () => {
+      return (
+        <div class={RootStyle}>
+          <Tooltip
+            visible={state.overlayVisible}
+            overlayClassName={OverlayClass}
+            destroyTooltipOnHide={true}
+            placement="rightTop"
+            trigger="click"
+            title={renderOverlay()}
+            onVisibleChange={toggleFilter}
+          >
+            <div
+              class={[
+                BtnStyle,
+                Object.keys(state.value).length != 0 ? "active" : null,
+              ]}
+            >
+              筛选 <DownOutlined style={{ fontSize: "12px" }} />
+            </div>
+          </Tooltip>
+        </div>
+      );
+    };
+  },
+});
+
+const RootStyle = css`
+  /* .ant-cascader-input.ant-input {
+    outline: none;
+    box-shadow: none;
+    border: 1px solid transparent;
+    padding-top: 2px;
+    padding-bottom: 2px;
+  }
+  .ant-cascader-picker-label {
+    font-size: 12px;
+    color: #4c4c4c;
+  }
+  .ant-cascader-picker-clear,
+  .ant-cascader-picker-arrow {
+    right: 10px;
+  } */
+`;
+const BtnStyle = css`
+  padding-left: 12px;
+  height: 32px;
+  line-height: 30px;
+  cursor: pointer;
+  color: rgba(255, 255, 255, 0.5);
+  border-radius: 2px;
+  border: 1px solid transparent;
+  background-color: #303030;
+  &.active {
+    background-color: rgba(234, 158, 64, 0.2);
+    color: @inf-primary-color;
+  }
+`;
+
+const OverlayClass = css`
+  max-width: 450px;
+  .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: 150px;
+      height: 280px;
+      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 20px;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+          &:hover {
+            background-color: #404040;
+          }
+          .arrow {
+            position: absolute;
+            right: 10px;
+            top: 50%;
+            transform: translateY(-50%);
+          }
+        }
+        .cate_text_item {
+          &.active {
+            &::after {
+              position: absolute;
+              top: 50%;
+              right: 30px;
+              width: 5px;
+              height: 9px;
+              display: block;
+              border: 1px solid #fff;
+              border-top: 0;
+              border-left: 0;
+              transform: rotate(45deg) scale(1) translateY(-9px);
+              opacity: 1;
+              transition: all 0.2s cubic-bezier(0.12, 0.4, 0.29, 1.46) 0.1s;
+              content: " ";
+            }
+          }
+        }
+      }
+    }
+  }
+  .overlay_footer {
+    text-align: right;
+    padding: 16px 20px;
+    border-top: 1px solid #1f1f1f;
+    .reset {
+      margin-right: 16px;
+      background-color: rgba(64, 64, 64, 1);
+      border-color: rgba(64, 64, 64, 1);
+      color: rgba(255, 255, 255, 1);
+      &:hover {
+        background-color: rgba(64, 64, 64, 0.8);
+        border-color: rgba(64, 64, 64, 0.8);
+        color: rgba(255, 255, 255, 0.8);
+      }
+      &:active {
+        background-color: rgba(64, 64, 64, 1);
+        border-color: rgba(64, 64, 64, 1);
+        color: rgba(255, 255, 255, 1);
+      }
+    }
+  }
+`;

+ 18 - 21
src/modules/editor/components/Viewport/Slider/SliderLeft/ListFilter/CateSelect.tsx

@@ -5,6 +5,7 @@ import { any, string } from "vue-types";
 
 export default defineComponent({
   props: {
+    label: string().isRequired,
     data: any().isRequired,
     value: any().isRequired,
   },
@@ -19,6 +20,7 @@ export default defineComponent({
       list = [...list];
       const item = list.pop();
       if (!item) return;
+
       if (item.id == value) {
         return [...pre, item];
       } else {
@@ -32,21 +34,19 @@ export default defineComponent({
       <Cascader
         class={rootStyle}
         options={props.data}
-        value={selected.value}
-        placeholder={"筛选"}
+        value={selected.value.map((d: any) => d.id)}
+        placeholder={props.label}
         expand-trigger="hover"
-        multiple={true}
-        // changeOnSelect={true}
+        changeOnSelect={true}
         fieldNames={{ label: "name", value: "id", children: "children" }}
         onChange={(values) => {
-          console.log(values);
-          emit("change", values);
+          emit("change", values ? values[values.length - 1] : undefined);
         }}
       >
         {{
           displayRender: () => {
             const value = selected.value[selected.value.length - 1]?.name;
-            return value ? value : "";
+            return value ? value : props.label;
           },
         }}
       </Cascader>
@@ -55,19 +55,16 @@ export default defineComponent({
 });
 
 const rootStyle = css`
-  /* .ant-cascader-input.ant-input {
-    outline: none;
-    box-shadow: none;
-    border: 1px solid transparent;
-    padding-top: 2px;
-    padding-bottom: 2px;
-  }
-  .ant-cascader-picker-label {
-    font-size: 12px;
-    color: #4c4c4c;
+  &.ant-cascader {
+    .ant-select-selector {
+      background-color: #303030;
+      border-color: transparent;
+    }
+    .ant-select-selection-placeholder {
+      color: rgba(255, 255, 255, 0.5);
+    }
+    .ant-select-clear {
+      color: rgba(255, 255, 255, 1);
+    }
   }
-  .ant-cascader-picker-clear,
-  .ant-cascader-picker-arrow {
-    right: 10px;
-  } */
 `;

+ 58 - 22
src/modules/editor/components/Viewport/Slider/SliderLeft/ListFilter/index.tsx

@@ -2,7 +2,9 @@ import { useEditor } from "@/modules/editor";
 import { css } from "@linaria/core";
 import { defineComponent, reactive } from "vue";
 import { bool, string } from "vue-types";
+import CateFilter from "./CateFilter";
 import CateSelect from "./CateSelect";
+import { cloneDeep } from "lodash";
 
 export default defineComponent({
   props: {
@@ -13,7 +15,8 @@ export default defineComponent({
   setup(props, { emit }) {
     const { controls } = useEditor();
     const state = reactive({
-      values: [] as any,
+      limitLen: 2,
+      values: {} as any,
     });
     const getCate = () => {
       const categories = props.isSys
@@ -25,34 +28,66 @@ export default defineComponent({
 
       return currCate?.children || [];
     };
-    const cateChange = (v: string) => {
-      state.values = v;
+    const cateChange = (id: string, v: string) => {
+      const values = cloneDeep(state.values);
+      if (!v && values[id]) {
+        delete values[id];
+      } else {
+        values[id] = v;
+      }
+      state.values = values;
+      const categories = Object.values(values);
+      emit(
+        "change",
+        categories && categories.length > 0 ? categories : undefined
+      );
+    };
+    const filterChange = (v: any) => {
+      let values = cloneDeep(state.values);
+      if (!v) {
+        emit("change", undefined);
+        state.values = {};
+        return;
+      }
+      values = { ...values, ...v };
+      Object.keys(values).map((key: any) => {
+        if (values[key] == undefined) {
+          delete values[key];
+        }
+      });
+      state.values = values;
+      const categories = Object.values(values);
+
+      emit(
+        "change",
+        categories && categories.length > 0 ? categories : undefined
+      );
     };
     return () => {
       const options = getCate();
       if (options.length == 0) return null;
       return (
         <div class={rootStyle}>
-          <CateSelect
-            data={options}
-            value={state.values}
-            onChange={(v) => cateChange(v)}
-          />
-          {/* {options.slice(0, 2).map((d: any, i: number) => (
-            <div class={"cate_item flex-1"}></div>
-          ))} */}
-          {/* <Tooltip
-            visible={state.overlayVisible}
-            placement="rightTop"
-            trigger="click"
-            title={renderOverlay()}
-            onVisibleChange={toggleFilter}
-          >
-            <div class={btnStyle}>
-              筛选
-              <Icon type="filter" size="0.14rem" />
+          {options.slice(0, state.limitLen).map((d: any, i: number) => (
+            <div class={"cate_item"}>
+              <CateSelect
+                key={i}
+                label={d.name}
+                data={d.children}
+                value={state.values[d.id]}
+                onChange={(v) => cateChange(d.id, v)}
+              />
+            </div>
+          ))}
+          {options.length > state.limitLen && (
+            <div class={"ml-10px flex-1"}>
+              <CateFilter
+                options={options}
+                value={state.values}
+                onChange={filterChange}
+              />
             </div>
-          </Tooltip> */}
+          )}
         </div>
       );
     };
@@ -68,6 +103,7 @@ const rootStyle = css`
   }
   .cate_item {
     margin-left: 10px;
+    width: 90px;
     &:first-child {
       margin-left: 0;
     }

+ 12 - 6
src/modules/editor/components/Viewport/Slider/SliderLeft/Sources/List.tsx

@@ -55,7 +55,15 @@ export const Sources = defineComponent({
         if (ctrl.state.list.length == 0) getData();
       }
     );
-
+    const filterChange = (v: any) => {
+      const ctrl = getCurrCtrl();
+      const query = ctrl.state.query || {};
+      ctrl.state.query = {
+        ...query,
+        categories: v,
+      };
+      ctrl.loadPage(1);
+    };
     return () => {
       const control = getCurrCtrl();
       const dataSource = control.state.list;
@@ -79,13 +87,11 @@ export const Sources = defineComponent({
             </div>
           )}
           <div>
-            {/* <ListFilter
+            <ListFilter
               sourceType={props.sourceType}
               isSys={props.sourceFrom == "user" ? false : true}
-              onChange={(v) => {
-                console.log(v);
-              }}
-            /> */}
+              onChange={filterChange}
+            />
             <Container
               class="grid grid-cols-2 gap-10px"
               behaviour="copy"

+ 7 - 4
src/pages/website/components/layout/LeftContent.tsx

@@ -7,6 +7,7 @@ import {
   IconSettings,
 } from "@queenjs/icons";
 import { Avatar, Divider } from "ant-design-vue";
+import { AppstoreOutlined } from "@ant-design/icons-vue";
 import { defineUI, queenApi } from "queenjs";
 import { defineComponent } from "vue";
 import { array, object } from "vue-types";
@@ -46,7 +47,7 @@ export default defineUI({
       {
         link: "/workbench/category",
         label: "我的分类",
-        icon: IconCube,
+        icon: AppstoreOutlined,
         // suffix: "32",
       },
       {
@@ -68,8 +69,8 @@ export default defineUI({
 
     return () => {
       const { userInfo } = props.Controller.state;
-      const  isSys = (userInfo.roles || []).indexOf("system") > -1;
-    
+      const isSys = (userInfo.roles || []).indexOf("system") > -1;
+
       return (
         <div class={cx(rootStyles, "px-25px py-16px h-1/1 flex flex-col")}>
           <div>
@@ -78,7 +79,9 @@ export default defineUI({
           <div class="my-50px flex items-center">
             <Avatar size={76} src={userInfo.avatar} />
             <div class="ml-20px flex-1">
-              <p class="mb-10px text-16px font-bold">{userInfo.name} {isSys?"(平台账号)":""}</p>
+              <p class="mb-10px text-16px font-bold">
+                {userInfo.name} {isSys ? "(平台账号)" : ""}
+              </p>
               <div
                 class="text-12px text-gray cursor-pointer"
                 style={{ color: "#E88B00" }}