Browse Source

collection

bianjiang 1 year ago
parent
commit
9c5d20b3db

+ 7 - 2
src/modules/editor/components/CompUI/basicUI/Page/PageMusic.tsx

@@ -11,6 +11,7 @@ import { css } from "@linaria/core";
 import { Button, Dropdown, Slider } from "ant-design-vue";
 import { Howl } from "howler";
 import { nanoid } from "nanoid";
+import { isPc } from "@queenjs/utils";
 import {
   defineComponent,
   reactive,
@@ -178,7 +179,12 @@ export const PageMusic = defineComponent({
     return () => {
       const music = rootComp?.value.music;
       return (
-        <div class={store.isEditMode ? MusicEditStyle : MusicStyle}>
+        <div
+          class={[
+            store.isEditMode ? MusicEditStyle : MusicStyle,
+            isPc() ? "absolute" : "fixed",
+          ]}
+        >
           {store.isEditMode ? (
             <AudioPlayer
               key={music}
@@ -399,7 +405,6 @@ const AudioPlayerStyle = css`
   }
 `;
 const MusicStyle = css`
-  position: fixed;
   top: 10px;
   right: 10px;
   z-index: 999;

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

@@ -15,7 +15,6 @@ export const Component = defineComponent({
     const editor = useEditor();
     const { helper } = editor;
     const compRef = useCompRef(props.compId);
-
     return () => {
       const { children, layout, value } = useCompData(props.compId);
       const compMusic = value.music || "";

+ 22 - 1
src/modules/resource/actions/collection.tsx

@@ -15,10 +15,31 @@ export const collectionAction = ResourceModule.action({
     const data = await this.showModal(
       <this.components.CollectionEditModal record={record} />
     );
-    if (record && record.id) {
+    if (record && record._id) {
       await this.https.updateCollection(data);
     } else {
       await this.https.createCollection(data);
     }
   },
+  async collectionDTL(id: string) {
+    const res = await this.https.detailCollection(id);
+    if (res.errorNo != 200) {
+      return;
+    }
+    return res.result;
+  },
+  async searchCollection(key: string) {
+    const res = await this.https.searchCollection({ key });
+    if (res.errorNo != 200) {
+      return;
+    }
+    return res.result;
+  },
+  async commitCollection(data: any) {
+    const res = await this.https.commitCollection(data);
+    if (res.errorNo != 200) {
+      return;
+    }
+    return res.result;
+  },
 });

+ 192 - 34
src/modules/resource/components/CollectionEditModal.tsx

@@ -1,17 +1,27 @@
+import { IconImage } from "@/assets/icons";
+import { useEditor } from "@/modules/editor";
+import { SelectOneImage } from "@/pages/website/Material2/modal";
+import { PlusOutlined } from "@ant-design/icons-vue";
 import { css } from "@linaria/core";
-import { Button, Form, Input, DatePicker } from "ant-design-vue";
-import dayjs, { Dayjs } from "dayjs";
-import { useModal } from "queenjs";
-import { defineComponent, reactive } from "vue";
-import { any, string } from "vue-types";
+import { IconAddLine } from "@queenjs/icons";
+import { Image } from "@queenjs/ui";
+import {
+  Button,
+  DatePicker,
+  Divider,
+  Form,
+  Input,
+  Select,
+} from "ant-design-vue";
 import locale from "ant-design-vue/es/date-picker/locale/zh_CN";
+import dayjs, { Dayjs } from "dayjs";
 import localeData from "dayjs/plugin/localeData";
 import weekday from "dayjs/plugin/weekday";
-import { Image } from "@queenjs/ui";
-import { IconImage } from "@/assets/icons";
-import { IconAddLine } from "@queenjs/icons";
-import { useEditor } from "@/modules/editor";
-import { SelectOneImage } from "@/pages/website/Material2/modal";
+import { queenApi, useModal } from "queenjs";
+import { defineComponent, reactive } from "vue";
+import { any, string } from "vue-types";
+import { useResource } from "..";
+
 const layout = {
   labelCol: { span: 8 },
   wrapperCol: { span: 16 },
@@ -33,17 +43,30 @@ export default defineComponent({
     const data = props.record
       ? { ...props.record }
       : {
-          thumbnail: "",
+          cover: "",
           logo: "",
-          url: "",
+          introLink: "",
+          introH5Id: "",
+          statues: [],
+          flags: [],
+          status: "",
           title: "",
           startTime: "",
           endTime: "",
         };
+
+    const resource = useResource();
+    resource.controls.promotionListCtrl.loadPage(1);
     const formState: { [name: string]: any } = reactive({
       ...data,
     });
 
+    const state = reactive({
+      linkType: "id",
+    });
+    if (data.introLink) {
+      state.linkType = "link";
+    }
     const rules = reactive({
       title: [
         { required: true, message: "标题不能为空", trigger: "change" },
@@ -54,11 +77,7 @@ export default defineComponent({
           trigger: "change",
         },
       ],
-      thumbnail: [
-        { required: true, message: "封面图不能为空", trigger: "change" },
-      ],
-      logo: [{ required: false }],
-      url: [{ required: false }],
+      cover: [{ required: true, message: "封面图不能为空", trigger: "change" }],
     });
 
     const { validate, validateInfos } = Form.useForm(formState, rules);
@@ -78,18 +97,69 @@ export default defineComponent({
         本月: [dayjs().startOf("month"), dayjs().endOf("month")] as RangeValue,
       };
     };
+
+    const statusOptions = () => {
+      const options = formState.statues.map((e: any) => {
+        return { label: e, value: e };
+      });
+      return options;
+    };
+    const addStatus = async () => {
+      const text = await queenApi.showInput({
+        title: "添加状态",
+        placeholder: "输入状态名称",
+      });
+      if (!text) return;
+      if (formState.statues.length == 0) {
+        formState.status = text;
+      }
+      formState.statues.push(text);
+    };
+    const flagsOptions = () => {
+      const options = formState.flags.map((e: any) => {
+        return { label: e, value: e };
+      });
+      return options;
+    };
+    const addFlag = async () => {
+      const text = await queenApi.showInput({
+        title: "添加标签",
+        placeholder: "输入标签名称",
+      });
+      if (!text) return;
+      formState.flags.push(text);
+    };
+    const h5Options = () => {
+      const options = resource.controls.promotionListCtrl.state.list.map(
+        (e: any) => {
+          return { label: e.title, value: e._id };
+        }
+      );
+      return options;
+    };
+    const scorllEnd = (e: any) => {
+      const { target } = e;
+      if (target.scrollTop + target.offsetHeight === target.scrollHeight) {
+        if (resource.controls.promotionListCtrl.state.canLoadNext) {
+          resource.controls.promotionListCtrl.loadNextPage();
+        }
+      }
+    };
     return () => {
+      const time: [Dayjs, Dayjs] | undefined =
+        formState.startTime && formState.endTime
+          ? [dayjs(formState.startTime), dayjs(formState.endTime)]
+          : undefined;
       return (
         <div class={ModalStyle}>
           <Form {...layout} onSubmit={submit}>
-            <Form.Item {...validateInfos.thumbnail} wrapperCol={{ span: 24 }}>
-              <div class={"w-full h-150px pt-20px"}>
+            <Form.Item {...validateInfos.cover} wrapperCol={{ span: 24 }}>
+              <div class={"w-full h-180px pt-20px"}>
                 <ImageUploader
-                  data={formState.thumbnail}
+                  data={formState.cover}
                   text="请选择封面图"
                   onChange={(v) => {
-                    formState.thumbnail = v;
-                    console.log(v);
+                    formState.cover = v;
                   }}
                 />
               </div>
@@ -100,13 +170,42 @@ export default defineComponent({
                 v-model={[formState.title, "value"]}
               />
             </Form.Item>
-            <Form.Item {...validateInfos.url} label="详情链接">
-              <Input
-                placeholder={"请输入链接地址"}
-                v-model={[formState.url, "value"]}
-              />
+            <Form.Item label="详情介绍">
+              <Input.Group compact>
+                <Select
+                  defaultValue={state.linkType}
+                  onChange={(v: any) => {
+                    state.linkType = v;
+                    if (v == "id") {
+                      formState.introLink = "";
+                    } else {
+                      formState.introH5Id = null;
+                    }
+                  }}
+                >
+                  <Select.Option value="id">站内作品</Select.Option>
+                  <Select.Option value="link">站外链接</Select.Option>
+                </Select>
+                {state.linkType == "link" ? (
+                  <Input
+                    class={"flex-1"}
+                    placeholder={"请输入链接地址"}
+                    v-model={[formState.introLink, "value"]}
+                  />
+                ) : (
+                  <Select
+                    value={formState.introH5Id}
+                    class={"flex-1"}
+                    options={h5Options()}
+                    onPopupScroll={scorllEnd}
+                    onChange={(v: any) => {
+                      formState.introH5Id = v;
+                    }}
+                  ></Select>
+                )}
+              </Input.Group>
             </Form.Item>
-            <Form.Item {...validateInfos.logo} label="logo">
+            <Form.Item label="logo">
               <div class={"w-80px h-80px"}>
                 <ImageUploader
                   data={formState.logo}
@@ -114,19 +213,73 @@ export default defineComponent({
                   icon={<IconAddLine class="text-18px" />}
                   onChange={(v) => {
                     formState.logo = v;
-                    console.log(v);
                   }}
                 />
               </div>
             </Form.Item>
-            <Form.Item label="开始/结束日期">
+            <Form.Item label={"自定义状态"}>
+              <Select
+                options={statusOptions()}
+                listHeight={150}
+                value={formState.status}
+                onChange={(v: any) => {
+                  formState.status = v;
+                }}
+              >
+                {{
+                  dropdownRender: ({ menuNode }: any) => {
+                    return (
+                      <>
+                        {menuNode}
+                        <Divider style={{ margin: 0 }} />
+                        <Button
+                          type="link"
+                          icon={<PlusOutlined />}
+                          onMousedown={(e) => {
+                            e.stopPropagation();
+                          }}
+                          onClick={addStatus}
+                        >
+                          添加状态
+                        </Button>
+                      </>
+                    );
+                  },
+                }}
+              </Select>
+            </Form.Item>
+            <Form.Item label={"自定义标签"}>
+              <Select options={flagsOptions()} listHeight={150}>
+                {{
+                  dropdownRender: ({ menuNode }: any) => {
+                    return (
+                      <>
+                        {menuNode}
+                        <Divider style={{ margin: 0 }} />
+                        <Button
+                          type="link"
+                          icon={<PlusOutlined />}
+                          onMousedown={(e) => {
+                            e.stopPropagation();
+                          }}
+                          onClick={addFlag}
+                        >
+                          添加标签
+                        </Button>
+                      </>
+                    );
+                  },
+                }}
+              </Select>
+            </Form.Item>
+            <Form.Item label="征集日期">
               <RangePicker
                 locale={dateLocal}
                 ranges={pickerRanges()}
+                value={time}
                 onChange={(datas: any) => {
-                  console.log(datas);
-                  formState.startTime = dayjs(datas[0]).format("YYYY-MM-DD");
-                  formState.endTime = dayjs(datas[1]).format("YYYY-MM-DD");
+                  formState.startTime = dayjs(datas[0]);
+                  formState.endTime = dayjs(datas[1]);
                 }}
               />
             </Form.Item>
@@ -203,4 +356,9 @@ const ImageStyle = css`
   }
 `;
 
-const ModalStyle = css``;
+const ModalStyle = css`
+  width: 480px;
+  .ant-input-group.ant-input-group-compact {
+    display: flex;
+  }
+`;

+ 3 - 0
src/modules/resource/controllers/CollectionController.ts

@@ -11,4 +11,7 @@ export class CollectionController {
   onEdit(item: any) {
     console.log(item);
   }
+  onPreview(item: any) {
+    console.log(item);
+  }
 }

+ 0 - 19
src/modules/resource/helper.ts

@@ -1,7 +1,5 @@
-import { PageListController } from "@queenjs/controllers";
 import { queenApi } from "queenjs";
 import { ResourceModule } from ".";
-import { CollectionController } from "./controllers/CollectionController";
 
 export const helper = ResourceModule.helper({
   createFileName(fileName: string, dir: string) {
@@ -14,23 +12,6 @@ export const helper = ResourceModule.helper({
     )}_${fileName}`;
   },
 
-  createCollectionController() {
-    const ctrl = new CollectionController();
-    ctrl.ListCtrl = new PageListController(this.config?.httpConfig);
-    ctrl.ListCtrl.setCrudPrefix("/frame");
-    ctrl.createCollection = async () => {
-      await this.actions.createOrUpdateCollection(null);
-    };
-    ctrl.onMenuClick = async (name: string, record: any) => {
-      if (name == "delete") {
-        await this.actions.deleteCollection(record);
-        ctrl.ListCtrl.fresh();
-      } else if (name == "update") {
-        await this.actions.createOrUpdateCollection(record);
-      }
-    };
-    return ctrl;
-  },
   // createSourceController() {
   //   const { controls, actions } = this;
 

+ 12 - 4
src/modules/resource/http.ts

@@ -48,15 +48,23 @@ export const http = ResourceModule.http({
   },
   //collection
   createCollection(data: any) {
-    return this.request("/frame/create", { method: "POST", data });
+    return this.request("/works/create", { method: "POST", data });
   },
 
   updateCollection(data: any) {
-    return this.request("/frame/update", { method: "POST", data });
+    return this.request("/works/update", { method: "POST", data });
   },
-
   deleteCollection(id: string) {
-    return this.request(`/frame/delete/${id}`, { method: "POST" });
+    return this.request(`/works/delete/${id}`, { method: "POST" });
+  },
+  detailCollection(id: string) {
+    return this.request(`/works/detail/${id}`, { method: "GET" });
+  },
+  searchCollection(params: any) {
+    return this.request("/works/search", { method: "GET", params });
+  },
+  commitCollection(data: any) {
+    return this.request("/works/commit", { method: "POST", data });
   },
 });
 

+ 7 - 20
src/pages/website/MyCollection/components/CompItem.tsx → src/pages/website/MyCollection/components/CollectionItem.tsx

@@ -17,25 +17,15 @@ export default defineUI({
       return (
         <div class={cx(itemStyles, "relative")}>
           <View ratio={1.4} class=" relative">
-            <Image
-              class="h-1/1 w-1/1 !object-contain bg-[#ebebeb]"
-              src={record?.thumbnail}
-            />
-            {/* <Tag
-              color="#E88B00"
-              // color="rgba(0, 0, 0, 0.4)"
-              class="absolute top-0 left-0 z-1 !rounded-none"
-            >
-              未发布
-            </Tag> */}
-            {/* <div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
+            <Image class="h-1/1 w-1/1  bg-[#ebebeb]" src={record?.cover} />
+            <div class="absolute inset-0 flex items-center justify-center opacity-0 hover:opacity-100 transition-opacity">
               <div
-                class="text-white icon_action w-60px leading-60px orange cursor-pointer rounded-1/2 text-center"
-                onClick={() => emit("edit", props.record)}
+                class="text-white icon_action w-60px leading-60px  cursor-pointer rounded-1/2 text-center transition-opacity hover:opacity-90 active:opacity-80"
+                onClick={() => emit("preview", props.record)}
               >
-                编辑
+                查看
               </div>
-            </div> */}
+            </div>
           </View>
           <div class="item_footer rounded-b-4px flex items-center justify-between p-15px">
             <div class="w-0 flex-1">
@@ -71,9 +61,6 @@ const itemStyles = css`
     background: #414141;
   }
   .icon_action {
-    background-color: rgba(0, 0, 0, 0.5);
-    &.orange {
-      background-color: rgba(232, 139, 0, 0.5);
-    }
+    background-color: rgba(0, 0, 0, 0.8);
   }
 `;

+ 3 - 3
src/pages/website/MyCollection/components/index.tsx

@@ -3,7 +3,7 @@ import { CollectionController } from "@/modules/resource/controllers/CollectionC
 import { defineUI } from "queenjs";
 import { onMounted } from "vue";
 import { any } from "vue-types";
-import CompItem from "./CompItem";
+import CollectionItem from "./CollectionItem";
 import Header from "./Header";
 
 export default defineUI({
@@ -33,12 +33,12 @@ export default defineUI({
             columns={5}
             control={props.Controller.ListCtrl}
             item={(record: any) => (
-              <CompItem
+              <CollectionItem
                 record={record}
                 onMenu={(name) => {
                   props.Controller.onMenuClick(name, record);
                 }}
-                onEdit={(record) => props.Controller.onEdit(record)}
+                onPreview={(record) => props.Controller.onPreview(record)}
               />
             )}
           />

+ 30 - 0
src/pages/website/MyCollection/controller.tsx

@@ -0,0 +1,30 @@
+import { ResourceModule } from "@/modules/resource";
+import { CollectionController } from "@/modules/resource/controllers/CollectionController";
+import { PageListController } from "@queenjs/controllers";
+
+export function createCollectionController(resource: ResourceModule) {
+  const ctrl = new CollectionController();
+
+  ctrl.ListCtrl = new PageListController(resource.config?.httpConfig);
+  ctrl.ListCtrl.setCrudPrefix("/works");
+
+  ctrl.createCollection = async () => {
+    await resource.actions.createOrUpdateCollection(null);
+  };
+  ctrl.onPreview = (record: any) => {
+    console.log(record);
+  };
+  ctrl.onMenuClick = async (name: string, record: any) => {
+    if (name == "delete") {
+      await resource.actions.deleteCollection(record);
+      ctrl.ListCtrl.fresh();
+    } else if (name == "update") {
+      if (!record._id) {
+        return;
+      }
+      const detail = await resource.actions.collectionDTL(record._id);
+      await resource.actions.createOrUpdateCollection(detail);
+    }
+  };
+  return ctrl;
+}

+ 5 - 4
src/pages/website/MyCollection/index.tsx

@@ -1,13 +1,14 @@
 import { useResource } from "@/modules/resource";
 import { defineComponent } from "vue";
 
-import PromotionUI from "./components";
+import CollectionUI from "./components";
+import { createCollectionController } from "./controller";
 export default defineComponent({
   setup() {
     const resource = useResource();
-    const ctrl = resource.helper.createCollectionController();
+    const ctrl = createCollectionController(resource);
     return () => (
-      <PromotionUI
+      <CollectionUI
         Controller={ctrl}
         slots={
           {
@@ -16,7 +17,7 @@ export default defineComponent({
             // }
           }
         }
-      ></PromotionUI>
+      ></CollectionUI>
     );
   },
 });

+ 96 - 20
src/pages/website/Promotion2/components/CollectionModal.tsx

@@ -1,26 +1,25 @@
 import { css } from "@linaria/core";
 
-import { Button, Select } from "ant-design-vue";
+import { Button, Input, Select } from "ant-design-vue";
 import { SearchOutlined } from "@ant-design/icons-vue";
 import { defineComponent, reactive } from "vue";
 import { any } from "vue-types";
+import { Image } from "@queenjs/ui";
+import { useResource } from "@/modules/resource";
 
+import dayjs from "dayjs";
+import { queenApi, useModal } from "queenjs";
 export default defineComponent({
   props: {
     record: any().isRequired,
   },
   setup(props, { slots }) {
-    // if (location.host == "www.infish.cn") {
-    //   shareLink =
-    //     location.origin +
-    //     "/projects/queenshowv1/share.html?id=" +
-    //     props.record._id;
-    // }
-
+    const resource = useResource();
     const state = reactive({
-      ...props.record,
+      currCollection: {} as any,
+      searchVal: "",
     });
-
+    const modal = useModal();
     const EmptyHistory = () => {
       return (
         <div
@@ -62,7 +61,65 @@ export default defineComponent({
         </div>
       );
     };
-
+    const itemRender = (data: any) => {
+      return (
+        <div class={"flex"}>
+          <Image class="w-160px h-160px rounded-6px" src={data?.thumbnail} />
+          <div class={"flex-1 pl-20px"}>
+            <div class={"text-18px"}>{data.title}</div>
+            <div class={"mt-14px text-gray"}>{data.desc}</div>
+          </div>
+        </div>
+      );
+    };
+    const CollectionRender = () => {
+      const data = state.currCollection;
+      return (
+        <div>
+          <div class={"w-full h-290px rounded-6px"}>
+            <Image class={"w-full h-full rounded-6px"} src={data.cover} />
+          </div>
+          <div class={"flex justify-between items-center mt-15px"}>
+            <div>
+              {data.logo && (
+                <Image
+                  class={"w-42px h-42px object-cover rounded-2px"}
+                  src={data.logo}
+                />
+              )}
+              {data.endTime && (
+                <span class={"pl-20px text-gray"}>
+                  截止日期:{dayjs(data.endTime).format("YYYY-MM-DD")}
+                </span>
+              )}
+            </div>
+            {data.status && (
+              <div
+                class={
+                  "text-16px text-white bg-blue-500 px-20px py-10px rounded-4px"
+                }
+              >
+                {data.status}
+              </div>
+            )}
+          </div>
+        </div>
+      );
+    };
+    const search = async () => {
+      const collection = await resource.actions.searchCollection(
+        state.searchVal
+      );
+      state.currCollection = collection[0];
+    };
+    const commit = async () => {
+      const res = await resource.actions.commitCollection({
+        id: state.currCollection._id,
+        ...props.record,
+      });
+      modal.submit();
+      queenApi.messageSuccess("发送成功");
+    };
     return () => {
       return (
         <div class={ModalStyle}>
@@ -72,25 +129,35 @@ export default defineComponent({
             >
               <div class={"text-16px"}>接收地址</div>
               <div class={"flex-1 px-30px"}>
-                <Select
+                <Input
                   class={"w-full text-center"}
                   placeholder={"请输入接收地址"}
-                  showSearch
-                  showArrow={false}
-                  notFoundContent={null}
-                ></Select>
+                  value={state.searchVal}
+                  onChange={(e) => {
+                    state.searchVal = e.target.value || "";
+                  }}
+                ></Input>
               </div>
               <div>
-                <Button type="primary" ghost icon={<SearchOutlined />}>
+                <Button
+                  type="primary"
+                  ghost
+                  icon={<SearchOutlined />}
+                  onClick={search}
+                >
                   查询
                 </Button>
               </div>
             </div>
-            <div class={"bg-white px-30px py-20px"}>{EmptyCollection()}</div>
+            <div class={"bg-white px-30px py-20px"}>
+              {state.currCollection._id
+                ? CollectionRender()
+                : EmptyCollection()}
+            </div>
             <div class={"flex items-center space-x-16px"}>
               <div class={"flex-1 bg-white px-30px py-18px rounded-6px"}>
                 <div class={"text-16px"}>当前发送</div>
-                <div class={"mt-20px h-160px"}></div>
+                <div class={"mt-20px h-160px"}>{itemRender(props.record)}</div>
               </div>
               <div class={"flex-1 bg-white px-30px py-18px rounded-6px"}>
                 <div class={"text-16px"}>历史发送</div>
@@ -104,7 +171,8 @@ export default defineComponent({
               ghost
               size={"large"}
               class={"w-240px"}
-              disabled={true}
+              disabled={state.currCollection._id ? false : true}
+              onClick={commit}
             >
               确认发送
             </Button>
@@ -116,6 +184,14 @@ export default defineComponent({
 });
 const ModalStyle = css`
   color: #111;
+  .ant-input {
+    border-radius: 4px;
+    border-color: rgba(51, 51, 51, 0.3);
+    color: #111;
+    &::placeholder {
+      color: #999;
+    }
+  }
   .ant-select:not(.ant-select-customize-input) {
     .ant-select-selector {
       border-radius: 4px;

+ 1 - 1
src/pages/website/Promotion2/components/ShareModal.tsx

@@ -43,7 +43,7 @@ export default defineComponent({
       return (
         <div class="flex items-start">
           <div>
-            <div class="scrollbar h-600px overflow-y-auto border-1px border-solid border-dark-50">
+            <div class="scrollbar h-600px relative overflow-y-auto border-1px border-solid border-dark-50">
               {slots.preview?.()}
             </div>
             <div class="mt-20px text-center">