bianjiang 1 年之前
父节点
当前提交
a9695ab646

+ 27 - 8
src/controllers/UploadController.ts

@@ -115,15 +115,34 @@ export class UploadController {
       return { error: "上传失败!" };
     }
   }
-  getBlobURLExt(url: string) {
-    const blob = this.blobURLMaps.get(url);
-    let ext = "unkown";
-    if (blob) {
+  async uploadFile(file: File): Promise<{ url?: string; error?: string }> {
+    const fromData = new FormData();
+    fromData.append("file", file);
+    try {
+      const ret = await this.request("/upload/file", {
+        method: "POST",
+        data: fromData,
+        headers: {
+          "Content-Type": "multipart/form-data",
+        },
+      });
+      if (ret.errorNo != 200) {
+        return { error: "上传失败!" };
+      }
+      const url = ret.result.url;
+      return { url };
+    } catch (e) {
+      return { error: "上传失败!" };
+    }
+  }
+  getFileExt(file: File) {
+    let ext: any = "unkown";
+    if (file) {
       const exp = /^.+\.(.+)$/;
-      if (blob.name && exp.test(blob.name)) {
-        ext = (exp.exec(blob.name) as any)[1];
-      } else if (blob.type) {
-        ext = blob.type.split("/").pop();
+      if (file.name && exp.test(file.name)) {
+        ext = (exp.exec(file.name) as any)[1];
+      } else if (file.type) {
+        ext = file.type.split("/").pop();
       }
       return ext.toLowerCase();
     } else {

+ 94 - 0
src/modules/admin/components/UploadFile.tsx

@@ -0,0 +1,94 @@
+import { FileOutlined, PlusOutlined } from "@ant-design/icons-vue";
+import { css } from "@linaria/core";
+import { defineComponent } from "vue";
+import { string } from "vue-types";
+import { uploader } from "../objects";
+
+import { message } from "ant-design-vue";
+
+export default defineComponent({
+  props: {
+    text: string(),
+    data: string(),
+  },
+  emits: ["change"],
+  setup(props, { emit }) {
+    const uploadFile = async () => {
+      const [file] = await uploader.selectFile({ accept: "*" });
+      if (file.size > 50 * 1024 * 1024) {
+        message.warn("文件不能超过50M!");
+        return false;
+      }
+      const fileRes = await uploader.uploadFile(file);
+      if (fileRes.error) {
+        message.error(fileRes.error);
+        return;
+      }
+      emit("change", fileRes.url);
+    };
+    const getFileName = (url: string) => {
+      const lastIndex = url.lastIndexOf("/");
+      if (lastIndex == -1) {
+        return "";
+      }
+      const file = url.substring(lastIndex + 1);
+      return file.split(".")[0];
+    };
+
+    return () => (
+      <div class={ImageStyle} onClick={uploadFile}>
+        <div class={"wapper"}>
+          {props.data ? (
+            <div class={"file"}>
+              <FileOutlined />
+              <div class={"up_txt"}>{getFileName(props.data)}</div>
+            </div>
+          ) : (
+            <div class={"no_value"}>
+              <PlusOutlined />
+              <div class={"up_txt"}>{props.text}</div>
+            </div>
+          )}
+        </div>
+      </div>
+    );
+  },
+});
+const ImageStyle = css`
+  width: 100%;
+  height: 100%;
+  color: #343434;
+  background-color: #f2f2f2;
+  font-size: 24px;
+  border-radius: 2px;
+  cursor: pointer;
+  .wapper {
+    width: 100%;
+    height: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  .file {
+    padding: 24px 0;
+    display: inline-flex;
+    flex-direction: column;
+    align-items: center;
+    .up_txt {
+      margin-top: 10px;
+      font-size: 14px;
+      color: #343434;
+    }
+  }
+  .no_value {
+    padding: 24px 0;
+    display: inline-flex;
+    flex-direction: column;
+    align-items: center;
+    .up_txt {
+      margin-top: 10px;
+      font-size: 14px;
+      color: #343434;
+    }
+  }
+`;

+ 8 - 2
src/modules/admin/components/UploadImage.tsx

@@ -14,9 +14,15 @@ export default defineComponent({
   emits: ["change"],
   setup(props, { emit }) {
     const uploadImage = async () => {
-      const [file] = await uploader.selectFile({ accept: "jpg,png,jpeg" });
+      const [file] = await uploader.selectFile();
+      const exts = ["jpg", "png", "jpeg"];
+      if (!exts.includes(uploader.getFileExt(file))) {
+        message.warn(`图片格式只支持${exts.join("/")}`);
+        return false;
+      }
       if (file.size > 5 * 1024 * 1024) {
-        return message.warn("图片不能超过5M!");
+        message.warn("图片不能超过5M!");
+        return false;
       }
       emit("change", uploader.createObjectURL(file));
     };

+ 0 - 14
src/modules/admin/https.ts

@@ -1,14 +0,0 @@
-import { request } from "./objects";
-
-export const uploadImage = (data: any) => {
-  return request("/upload/image", {
-    method: "POST",
-    data,
-  });
-};
-export const uploadFile = (data: any) => {
-  return request("/upload/file", {
-    method: "POST",
-    data,
-  });
-};

+ 4 - 0
src/views/admin/detail/components/DetailEditor.tsx

@@ -39,6 +39,7 @@ export default defineComponent({
     };
     const editorChange = ({ html, text }: any) => {
       state.data.content = html;
+      state.data.summary = text;
     };
     const submit = () => {
       const subData = { ...state.data };
@@ -46,6 +47,9 @@ export default defineComponent({
         subData.title = data?.name;
         subData.cid = data._id;
       }
+      let summary = state.data.summary.substring(0, 70);
+      summary = summary.replace(/\n/g, "");
+      state.data.summary = summary;
       artStore.addOrUpdateArticle(subData);
     };
 

+ 131 - 0
src/views/admin/detail/components/DownloadEditor.tsx

@@ -0,0 +1,131 @@
+import { Button, PageHeader, Space, Table, message } from "ant-design-vue";
+import { defineComponent, onMounted } from "vue";
+
+import loading from "@/components/Provider/Loading";
+import Modal from "@/components/Provider/Modal";
+import { useArticle } from "@/modules/admin";
+import { CategoryItem } from "@/typings/asset";
+import * as dayjs from "dayjs";
+import { object } from "vue-types";
+import DownloadModal from "./DownloadModal";
+
+export default defineComponent({
+  props: {
+    data: object<CategoryItem>(),
+  },
+  setup(props) {
+    const data: any = props.data || {};
+    const artStore = useArticle();
+
+    onMounted(() => {
+      initList();
+    });
+
+    const initList = async () => {
+      loading.show("");
+      artStore.listController.state.query = JSON.stringify({ cid: data._id });
+      await artStore.listController.loadPage(1);
+      loading.hidden();
+    };
+    const columns = [
+      {
+        title: "名称",
+        dataIndex: "title",
+      },
+      {
+        title: "创建时间",
+        dataIndex: "createTime",
+        customRender({ record }: any) {
+          return dayjs(record.createTime).format("YYYY-MM-DD");
+        },
+      },
+      {
+        title: "操作",
+        customRender: ({ record }: any) => {
+          return (
+            <Space>
+              <Button
+                type="link"
+                onClick={() => {
+                  editItem(record);
+                  console.log(record);
+                }}
+              >
+                编辑
+              </Button>
+              <Button
+                danger
+                type="link"
+                onClick={() => {
+                  artStore.deleteArticle(record);
+                  console.log(record);
+                }}
+              >
+                删除
+              </Button>
+            </Space>
+          );
+        },
+      },
+    ];
+
+    const editItem = async (item?: any) => {
+      let listItem = undefined;
+      if (item._id) {
+        const res = await artStore.listController.itemDetail(item._id);
+        if (res.errorNo != 200) {
+          message.warn("未查询到数据!");
+          return;
+        }
+        listItem = res.result;
+      }
+
+      const itemData: any = await Modal.show(
+        <DownloadModal data={listItem} />,
+        {
+          title: item._id ? `编辑${data?.name}文件` : `添加${data?.name}文件`,
+        }
+      );
+      if (!itemData._id) {
+        itemData.cid = data._id;
+      }
+      artStore.addOrUpdateArticle(itemData);
+    };
+
+    return () => {
+      return (
+        <div>
+          <PageHeader title={data?.name}>
+            {{
+              extra: () => {
+                return (
+                  <Button
+                    type="primary"
+                    onClick={() => {
+                      editItem({});
+                    }}
+                  >
+                    添加{data?.name}文件
+                  </Button>
+                );
+              },
+            }}
+          </PageHeader>
+          <Table
+            bordered
+            size="small"
+            pagination={{
+              size: "small",
+              showSizeChanger: false,
+              pageSize: artStore.listController.state.size,
+              total: artStore.listController.state.total,
+              onChange: (v) => artStore.listController.loadPage(v),
+            }}
+            columns={columns}
+            dataSource={artStore.listController.state.list}
+          ></Table>
+        </div>
+      );
+    };
+  },
+});

+ 112 - 0
src/views/admin/detail/components/DownloadModal.tsx

@@ -0,0 +1,112 @@
+import Modal from "@/components/Provider/Modal";
+import UploadFile from "@/modules/admin/components/UploadFile";
+import { css } from "@linaria/core";
+import { Button, Form, Input } from "ant-design-vue";
+import { defineComponent, reactive } from "vue";
+import { any } from "vue-types";
+
+const layout = {
+  labelCol: { span: 6 },
+  wrapperCol: { span: 18 },
+};
+
+export default defineComponent({
+  props: {
+    data: any(),
+  },
+  setup(props) {
+    const modal = Modal.use();
+    const formState: { [name: string]: any } = reactive({
+      formData: {
+        ...{
+          title: "",
+          content: "",
+          cover: "",
+          summary: "",
+          sort: 0,
+        },
+        ...props.data,
+      },
+    });
+
+    const rules = reactive({
+      title: [{ required: true, message: "名称不能为空", trigger: "change" }],
+      content: [{ required: true, message: "文件不能为空", trigger: "change" }],
+    });
+
+    const { validate, validateInfos } = Form.useForm(formState.formData, rules);
+
+    function submit() {
+      validate().then(async () => {
+        modal.submit(formState.formData);
+      });
+    }
+    const changeFile = (v: string) => {
+      formState.formData.content = v;
+    };
+
+    return () => {
+      return (
+        <div class={EditStyle}>
+          <div class={"edit_content"}>
+            <div class={"form_content"}>
+              <Form {...layout} onSubmit={submit}>
+                <Form.Item {...validateInfos.title} label={"文件名称"}>
+                  <Input
+                    placeholder={"请输入文件名称"}
+                    v-model={[formState.formData.title, "value"]}
+                    maxlength={30}
+                  />
+                </Form.Item>
+                <Form.Item {...validateInfos.content} label={"文件"}>
+                  <UploadFile
+                    data={formState.formData.content}
+                    text={"上传文件"}
+                    onChange={changeFile}
+                  ></UploadFile>
+                </Form.Item>
+                <Form.Item
+                  style={{ marginBottom: 0 }}
+                  wrapperCol={{ span: 24 }}
+                >
+                  <Button type="primary" htmlType="submit" block>
+                    保存
+                  </Button>
+                </Form.Item>
+              </Form>
+            </div>
+          </div>
+        </div>
+      );
+    };
+  },
+});
+const EditStyle = css`
+  width: 500px;
+  .edit_content {
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+  }
+  .comp_content {
+    flex: 1;
+    height: 0;
+    overflow: hidden;
+
+    .ant-tabs {
+      height: 100%;
+      .ant-tabs-nav {
+        .ant-tabs-nav-wrap {
+          overflow-x: auto;
+        }
+      }
+      .ant-tabs-tab {
+        user-select: none;
+      }
+      .ant-tabs-content {
+        height: 100%;
+        overflow-y: hidden;
+      }
+    }
+  }
+`;

+ 4 - 0
src/views/admin/detail/components/ListEditModal.tsx

@@ -8,6 +8,7 @@ import { css } from "@linaria/core";
 import { object } from "vue-types";
 import WangEditor from "../../components/WangEditor";
 import { useArticle } from "@/modules/admin";
+import { uploader } from "@/modules/admin/objects";
 
 export default defineComponent({
   props: {
@@ -46,7 +47,10 @@ export default defineComponent({
           state.formData.cover = artStore.getContentFirstImage(
             state.formData.content
           );
+        } else {
+          await uploader.uploadBlobImages(state.formData);
         }
+
         let summary = state.formData.summary.substring(0, 70);
         summary = summary.replace(/\n/g, "");
         state.formData.summary = summary;

+ 4 - 0
src/views/admin/detail/index.tsx

@@ -7,6 +7,7 @@ import { useRoute } from "vue-router";
 import { CategoryItem } from "@/typings/asset";
 import DetailEditor from "./components/DetailEditor";
 import ListEditor from "./components/ListEditor";
+import DownloadEditor from "./components/DownloadEditor";
 
 export default defineComponent({
   setup() {
@@ -29,6 +30,9 @@ export default defineComponent({
           {currCategory?.type == "list" && (
             <ListEditor key={currCategory._id} data={currCategory} />
           )}
+          {currCategory?.type == "download" && (
+            <DownloadEditor key={currCategory._id} data={currCategory} />
+          )}
         </Card>
       );
     };