zoukankan      html  css  js  c++  java
  • abp.NetCore使用vue.js前端配置菜单路由、列表页、编辑页、ViewUI上传文件、ElementUI列表、富文本编辑器vue-quill-editor

    配置路由:router/router.ts,在appRouters对象中copy修改就行了,注意所有路由包括父子路由的name都不能重复。
    路由配置中permission是对应的后台权限名称字符串:permission:'Pages.Articles',meta》title对应的是多语言配置key:Framework.Core》Localization》Framework.xml
    添加新页面:
    1、添加store实体:store/entities

    import Entity from './entity'
    export default class Article extends Entity<number>{
        isActive:boolean;
        tenantId:number;
        title:string;
        author:number;
        contents:string;
        articleType:number;
        coverImg:string;
    }

    2、添加store/modules,其中引用刚刚添加的实体类型

    import {Store,Module,ActionContext} from 'vuex'
    import ListModule from './list-module'
    import ListState from './list-state'
    import Article from '../entities/article'
    import Role from '../entities/role'
    import Ajax from '../../lib/ajax'
    import PageResult from '@/store/entities/page-result';
    import ListMutations from './list-mutations'
    
    interface ArticleState extends ListState<Article>{
        editArticle:Article
    }
    class ArticleMutations extends ListMutations<Article>{
    
    }
    class ArticleModule extends ListModule<ArticleState,any,Article>{
        state={
            totalCount:0,
            currentPage:1,
            pageSize:10,
            list: new Array<Article>(),
            loading:false,
            editArticle:new Article()
        }
        actions={
            async getAll(context:ActionContext<ArticleState,any>,payload:any){
                context.state.loading=true;
                let reponse=await Ajax.post('/api/services/app/Article/GetArticles',payload.data);//Ajax.get 方式,传参:{params:payload.data};Ajax.post 方式参数直接写 payload.data
                context.state.loading=false;
                let page=reponse.data.result as PageResult<Article>;
                context.state.totalCount=page.totalCount;
                context.state.list=page.items;
            },
            async create(context:ActionContext<ArticleState,any>,payload:any){
                await Ajax.post('/api/services/app/Article/Create',payload.data);
            },
            async update(context:ActionContext<ArticleState,any>,payload:any){
                await Ajax.put('/api/services/app/Article/Update',payload.data);
            },
            async delete(context:ActionContext<ArticleState,any>,payload:any){
                await Ajax.delete('/api/services/app/Article/Delete?Id='+payload.data.id);
            },
            async get(context:ActionContext<ArticleState,any>,payload:any){
                let reponse=await Ajax.get('/api/services/app/Article/Get?Id='+payload.id);
                return reponse.data.result as Article;
            },
        };
        mutations={
            setCurrentPage(state:ArticleState,page:number){
                state.currentPage=page;
            },
            setPageSize(state:ArticleState,pagesize:number){
                state.pageSize=pagesize;
            },
            edit(state:ArticleState,article:Article){
                state.editArticle=article;
            }
        }
    }
    const articleModule=new ArticleModule();
    export default articleModule;

    可以看到他都是用store来存储数据的。
    3、添加页面views/article/index.vue,这用的是切换组件,不跳转,就不用改变路径,也不用再多配置路由

    <template>
      <component :is="currentRouter" :operation='currentOpt' :mId="currentId" />
    </template>
    <script> 
    // 列表页面
    import list from '@/views/article/components/list'
    // 编辑页面
    import edit from '@/views/article/components/edit'
    
    export default {
      name: 'articles',
      components: { list, edit },
      data() {
        return {
          // 当前加载的组件,默认为 list 组件(显示列表页面)
          currentRouter: "list",
          currentOpt: undefined,
          currentId: undefined
        }
      },
      created() {
        
      }
    }
    </script>

    4、添加组件views/article/components/list.vue和edit.vue
    list.vue

    <template>
      <div>
        <Card dis-hover>
          <div class="page-body">
            <Form ref="queryForm" :label-width="80" label-position="left" inline>
              <Row :gutter="16">
                <Col span="6">
                  <FormItem :label="L('Keyword') + ':'" style=" 100%">
                    <Input
                      v-model="pagerequest.keyword"
                      :placeholder="L('ArticleTitle')"
                    />
                  </FormItem>
                </Col>
              </Row>
              <Row>
                <Button
                  @click="create"
                  icon="android-add"
                  type="primary"
                  size="large"
                  >{{ L("Add") }}</Button
                >
                <Button
                  icon="ios-search"
                  type="primary"
                  size="large"
                  @click="getpage"
                  class="toolbar-btn"
                  >{{ L("Find") }}</Button
                >
              </Row>
            </Form>
            <div class="margin-top-10">
              <Table
                :loading="loading"
                :columns="columns"
                :no-data-text="L('NoDatas')"
                border
                :data="list"
              >
              </Table>
              <Page
                show-sizer
                class-name="fengpage"
                :total="totalCount"
                class="margin-top-10"
                @on-change="pageChange"
                @on-page-size-change="pagesizeChange"
                :page-size="pageSize"
                :current="currentPage"
              ></Page>
            </div>
          </div>
        </Card>
      </div>
    </template>
    <script lang='ts'>
    import { Component, Vue, Inject, Prop, Watch } from "vue-property-decorator";
    import Util from "@/lib/util";
    import AbpBase from "@/lib/abpbase";
    import PageRequest from "@/store/entities/page-request";
    
    class PageModelRequest extends PageRequest {
      keyword: string;
      isActive: boolean = null; //nullable
    }
    
    @Component({
      // components: { CreateUser, EditUser },
    })
    export default class AbpArticles extends AbpBase {
      // createModalShow: boolean = false;
      // editModalShow: boolean = false;
      pagerequest: PageModelRequest = new PageModelRequest();
      // creationTime: Date[] = [];
    
      edit() {
        // this.editModalShow=true;
      }
      get list() {
        return this.$store.state.article.list;
      }
      get loading() {
        return this.$store.state.article.loading;
      }
      create() {
        this.$parent.currentOpt = "create";
        this.$parent.currentRouter = "edit";
      }
      isActiveChange(val: string) {
        console.log(val);
        if (val === "Actived") {
          this.pagerequest.isActive = true;
        } else if (val === "NoActive") {
          this.pagerequest.isActive = false;
        } else {
          this.pagerequest.isActive = null;
        }
      }
      pageChange(page: number) {
        this.$store.commit("article/setCurrentPage", page);
        this.getpage();
      }
      pagesizeChange(pagesize: number) {
        this.$store.commit("article/setPageSize", pagesize);
        this.getpage();
      }
      async getpage() {
        this.pagerequest.maxResultCount = this.pageSize;
        this.pagerequest.skipCount = (this.currentPage - 1) * this.pageSize;
        //filters
    
        // if (this.creationTime.length > 0) {
        //   this.pagerequest.from = this.creationTime[0];
        // }
        // if (this.creationTime.length > 1) {
        //   this.pagerequest.to = this.creationTime[1];
        // }
    
        await this.$store.dispatch({
          type: "article/getAll",
          data: this.pagerequest,
        });
      }
      get pageSize() {
        return this.$store.state.article.pageSize;
      }
      get totalCount() {
        return this.$store.state.article.totalCount;
      }
      get currentPage() {
        return this.$store.state.article.currentPage;
      }
    
      columns = [
        {
          title: this.L("ArticleTitle"),
          key: "title",
        },
        {
          title: this.L("ArticleAuthor"),
          key: "author",
        },
        {
          title: this.L("IsActive"),
          render: (h: any, params: any) => {
            return h("span", params.row.isActive ? this.L("Yes") : this.L("No"));
          },
        },
        {
          title: this.L("Actions"),
          key: "Actions",
           150,
          render: (h: any, params: any) => {
            return h("div", [
              h(
                "Button",
                {
                  props: {
                    type: "primary",
                    size: "small",
                  },
                  style: {
                    marginRight: "5px",
                  },
                  on: {
                    click: () => {
                      this.$store.commit("article/edit", params.row);
                      this.$parent.currentOpt = "edit";
                      this.$parent.currentRouter = "edit";
                      this.$parent.currentId = params.row.id;
                    },
                  },
                },
                this.L("Edit")
              ),
              h(
                "Button",
                {
                  props: {
                    type: "error",
                    size: "small",
                  },
                  on: {
                    click: async () => {
                      this.$Modal.confirm({
                        title: this.L("Tips"),
                        content: this.L("DeleteUserConfirm"),
                        okText: this.L("Yes"),
                        cancelText: this.L("No"),
                        onOk: async () => {
                          await this.$store.dispatch({
                            type: "article/delete",
                            data: params.row,
                          });
                          await this.getpage();
                        },
                      });
                    },
                  },
                },
                this.L("Delete")
              ),
            ]);
          },
        },
      ];
      async created() {
        this.getpage();
      }
    }
    </script>
    View Code

    edit.vue

    <template>
      <div>
        <Form ref="subForm" label-position="top" :rules="rules" :model="formModel">
          <Tabs value="detail">
            <TabPane :label="L('ArticleDetails')" name="detail">
              <FormItem :label="L('ArticleTitle')" prop="title">
                <Input
                  v-model="formModel.title"
                  :maxlength="32"
                  :minlength="2"
                ></Input>
              </FormItem>
              <FormItem :label="L('ArticleType')" prop="articleType">
                <Select
                  :placeholder="L('ArticleType')"
                  v-model="formModel.articleType"
                >
                  <Option
                    v-for="item in articleTypeDataItems"
                    :key="item.id"
                    :label="item.name"
                    :value="item.id"
                    >{{ item.name }}</Option
                  >
                </Select>
              </FormItem>
              <FormItem :label="L('ArticleCoverImg')" prop="coverImg">
                <Input v-model="formModel.coverImg"></Input>
                <Upload
                  name="coverImg"
                  ref="upload"
                  :show-upload-list="false"
                  :format="['jpg', 'jpeg', 'png']"
                  :max-size="2048"
                  :on-format-error="handleFormatError"
                  :on-exceeded-size="handleMaxSize"
                  :before-upload="handleBeforeUpload"
                  :on-success="handleSuccess"
                  :multiple="false"
                  type="drag"
                  action="http://localhost:21021/api/FileCommon/UploadFile"
                >
                  <Button icon="ios-cloud-upload-outline">{{
                    L("UploadFiles")
                  }}</Button>
                </Upload>
              </FormItem>
              <FormItem :label="L('ArticleAuthor')" prop="author">
                <Input v-model="formModel.author" :maxlength="32"></Input>
              </FormItem>
              <FormItem>
                <Checkbox v-model="formModel.isActive">{{
                  L("IsActive")
                }}</Checkbox>
              </FormItem>
              <FormItem :label="L('ArticleContents')" prop="contents">
                <quill-editor
                  v-model="formModel.contents"
                  ref="myQuillEditor"
                  style="height: 500px;margin-bottom:30px;"
                  :options="editorOption"
                >
                  <!-- 自定义toolar -->
                  <div id="toolbar" slot="toolbar">
                    <!-- Add a bold button -->
                    <button class="ql-bold" title="加粗">Bold</button>
                    <button class="ql-italic" title="斜体">Italic</button>
                    <button class="ql-underline" title="下划线">underline</button>
                    <button class="ql-strike" title="删除线">strike</button>
                    <button class="ql-blockquote" title="引用"></button>
                    <button class="ql-code-block" title="代码"></button>
                    <button class="ql-header" value="1" title="标题1"></button>
                    <button class="ql-header" value="2" title="标题2"></button>
                    <!--Add list -->
                    <button
                      class="ql-list"
                      value="ordered"
                      title="有序列表"
                    ></button>
                    <button
                      class="ql-list"
                      value="bullet"
                      title="无序列表"
                    ></button>
                    <!-- Add font size dropdown -->
                    <select class="ql-header" title="段落格式">
                      <option selected>段落</option>
                      <option value="1">标题1</option>
                      <option value="2">标题2</option>
                      <option value="3">标题3</option>
                      <option value="4">标题4</option>
                      <option value="5">标题5</option>
                      <option value="6">标题6</option>
                    </select>
                    <select class="ql-size" title="字体大小">
                      <option value="10px">10px</option>
                      <option value="12px">12px</option>
                      <option value="14px">14px</option>
                      <option value="16px" selected>16px</option>
                      <option value="18px">18px</option>
                      <option value="20px">20px</option>
                    </select>
                    <select class="ql-font" title="字体">
                      <option value="SimSun">宋体</option>
                      <option value="SimHei">黑体</option>
                      <option value="Microsoft-YaHei">微软雅黑</option>
                      <option value="KaiTi">楷体</option>
                      <option value="FangSong">仿宋</option>
                      <option value="Arial">Arial</option>
                    </select>
                    <!-- Add subscript and superscript buttons -->
                    <select
                      class="ql-color"
                      value="color"
                      title="字体颜色"
                    ></select>
                    <select
                      class="ql-background"
                      value="background"
                      title="背景颜色"
                    ></select>
                    <select class="ql-align" value="align" title="对齐"></select>
                    <button class="ql-clean" title="还原"></button>
                    <!-- You can also add your own -->
                  </div>
                </quill-editor>
              </FormItem>
            </TabPane>
          </Tabs>
        </Form>
        <div slot="footer" >
          <Button @click="cancel">{{ L("Cancel") }}</Button>
          &nbsp;&nbsp;&nbsp;
          <Button @click="save" type="primary">{{ L("OK") }}</Button>
        </div>
      </div>
    </template>
    <script lang="ts">
    import { Quill, quillEditor } from "vue-quill-editor";
    import "quill/dist/quill.core.css";
    import "quill/dist/quill.snow.css";
    import "quill/dist/quill.bubble.css";
    //引入font.css
    import "@/assets/css/font.css";
    
    // 自定义字体大小
    let Size = Quill.import("attributors/style/size");
    Size.whitelist = ["10px", "12px", "14px", "16px", "18px", "20px"];
    Quill.register(Size, true);
    
    // 自定义字体类型
    var fonts = [
      "SimSun",
      "SimHei",
      "Microsoft-YaHei",
      "KaiTi",
      "FangSong",
      "Arial",
      "Times-New-Roman",
      "sans-serif",
      "宋体",
      "黑体",
    ];
    var Font = Quill.import("formats/font");
    Font.whitelist = fonts;
    Quill.register(Font, true);
    
    import { ArticleTypeDataItems } from "@/lib/constData";
    import { Component, Vue, Inject, Prop, Watch } from "vue-property-decorator";
    import Util from "../../../lib/util";
    import AbpBase from "../../../lib/abpbase";
    import Article from "@/store/entities/article";
    
    @Component({
      components: { quillEditor },
    })
    export default class EditArticle extends AbpBase {
      @Prop({ type: Boolean, default: false }) value: boolean;
      formModel: Article = new Article();
      articleTypeDataItems = ArticleTypeDataItems;
      uploadFormat = [".jpg", ".png", ".jpeg"];
      editorOption = {
        placeholder: "请输入",
        theme: "snow", // or 'bubble'
        modules: {
          toolbar: {
            container: "#toolbar",
          },
        },
      };
      handleSuccess(res, file) {
        console.log(res.data);
        file.url = res.data.fileUrl;
        file.name = res.data.fileUrl;
        this.formModel.coverImg = res.data.fileUrl;
      }
      handleFormatError(file) {
        this.$Notice.warning({
          title: "The file format is incorrect",
          desc:
            "File format of " +
            file.name +
            " is incorrect, please select jpg or png.",
        });
      }
      handleMaxSize(file) {
        this.$Notice.warning({
          title: "Exceeding file size limit",
          desc: "File  " + file.name + " is too large, no more than 2M.",
        });
      }
      handleBeforeUpload() {
        //上传文件之前的逻辑处理,return false 阻止上传
        const check = false;
        if (!check) {
          this.$Notice.warning({
            title: "error,can't upload",
          });
        }
        return check;
      }
      created() {
        let editModel = null;
        if (this.$store.state.article.editArticle) {
          editModel = this.$store.state.article.editArticle;
        }
        this.formModel = Util.extend(true, {}, editModel);
      }
      // articleTypeChange(val: string) {
      //   this.formModel.articleType = parseInt(val);
      // }
      save() {
        (this.$refs.subForm as any).validate(async (valid: boolean) => {
          if (valid) {
            let typeName = "article/create";
            if (this && this.formModel && this.formModel.id) {
              typeName = "article/update";
            }
            await this.$store.dispatch({
              type: typeName,
              data: this.formModel,
            });
            (this.$refs.subForm as any).resetFields();
            this.$emit("save-success");
            this.$emit("input", false);
            this.$Notice.success({
              title: "tips",
              desc: "success",
            });
          }
        });
      }
      cancel() {
        this.$parent.currentOpt = "list";
        this.$parent.currentRouter = "list";
        this.$parent.currentId = 0;
      }
      rules = {
        title: [
          {
            required: true,
            message: this.L("FieldIsRequired", undefined, this.L("ArticleTitle")),
            trigger: "blur",
          },
        ],
        author: [
          {
            required: true,
            message: this.L("FieldIsRequired", undefined, this.L("ArticleAuthor")),
            trigger: "blur",
          },
        ],
        contents: [
          {
            required: true,
            message: this.L(
              "FieldIsRequired",
              undefined,
              this.L("ArticleContents")
            ),
            trigger: "blur",
          },
        ],
        articleType: [
          {
            required: true,
            message: this.L("FieldIsRequired", undefined, this.L("ArticleType")),
            trigger: "change",
            type: "number",
          },
        ],
      };
    }
    </script>
    View Code

    list.vue使用ElementUI版本,main.ts中增加引用和基本样式:import ElementUI from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';这个样式路径下面还有其他相关样式,需要可以自己引用一下。

    <template>
      <div>
        <div class="app-container">
          <div class="filter-container">
            <el-row :gutter="15">
              <el-col :md="6">
                <el-input
                  v-model="pagerequest.keyword"
                  :placeholder="L('ArticleTitle')"
                  class="filter-item"
                  size="mini"
                />
              </el-col>
              <el-col :md="6">
                <el-button
                  @click="create"
                  icon="android-add"
                  type="primary"
                  size="mini"
                  >{{ L("Add") }}</el-button
                >
                <el-button
                  class="filter-item"
                  type="primary"
                  icon="el-icon-search"
                  @click="getpage"
                  size="mini"
                  >{{ L("Find") }}</el-button
                >
              </el-col>
            </el-row>
          </div>
    
          <el-table
            v-loading="loading"
            :data="list"
            fit
            highlight-current-row
            style=" 100%"
            class="table-scroll-x"
          >
            <el-table-column label="ID" prop="id"></el-table-column>
            <el-table-column label="标题" prop="title"></el-table-column>
            <el-table-column label="作者" prop="author"></el-table-column>
            <el-table-column label="操作" class-name="small-padding">
              <template slot-scope="{ row }">
                <span class="link-type" @click="handleEdit(row)">编辑</span>
                <span class="link-type" @click="handleDelete(row)">删除</span>
              </template>
            </el-table-column>
          </el-table>
    
          <pagination
            v-show="totalCount > 0"
            :total="totalCount"
            :page="currentPage"
            :limit="pageSize"
            @pagination="pageChange"
          />
        </div>
      </div>
    </template>
    <script lang='ts'>
    import { Component, Vue, Inject, Prop, Watch } from "vue-property-decorator";
    import Pagination from "@/components/Pagination/index";
    import Util from "@/lib/util";
    import AbpBase from "@/lib/abpbase";
    import PageRequest from "@/store/entities/page-request";
    
    class PageModelRequest extends PageRequest {
      keyword: string;
      isActive: boolean = null; //nullable
    }
    
    @Component({
      components: { Pagination },
    })
    export default class AbpArticles extends AbpBase {
      // createModalShow: boolean = false;
      // editModalShow: boolean = false;
      pagerequest: PageModelRequest = new PageModelRequest();
      // creationTime: Date[] = [];
      handleEdit(row) {
        this.$store.commit("article/edit", row);
        this.$parent.currentOpt = "edit";
        this.$parent.currentRouter = "edit";
        this.$parent.currentId = row.id;
      }
      handleDelete(row) {
        this.$Modal.confirm({
          title: this.L("Tips"),
          content: this.L("DeleteUserConfirm"),
          okText: this.L("Yes"),
          cancelText: this.L("No"),
          onOk: async () => {
            await this.$store.dispatch({
              type: "article/delete",
              data: row,
            });
            await this.getpage();
          },
        });
      }
      edit() {
        // this.editModalShow=true;
      }
      get list() {
        return this.$store.state.article.list;
      }
      get loading() {
        return this.$store.state.article.loading;
      }
      create() {
        this.$parent.currentOpt = "create";
        this.$parent.currentRouter = "edit";
      }
      isActiveChange(val: string) {
        if (val === "Actived") {
          this.pagerequest.isActive = true;
        } else if (val === "NoActive") {
          this.pagerequest.isActive = false;
        } else {
          this.pagerequest.isActive = null;
        }
      }
      pageChange(page: number, pagesize: number) {
        this.$store.commit("article/setCurrentPage", page);
        this.$store.commit("article/setPageSize", pagesize);
        this.getpage();
      }
      pagesizeChange(pagesize: number) {
        this.$store.commit("article/setPageSize", pagesize);
        this.getpage();
      }
      async getpage() {
        this.pagerequest.maxResultCount = this.pageSize;
        this.pagerequest.skipCount = (this.currentPage - 1) * this.pageSize;
        //filters
    
        // if (this.creationTime.length > 0) {
        //   this.pagerequest.from = this.creationTime[0];
        // }
        // if (this.creationTime.length > 1) {
        //   this.pagerequest.to = this.creationTime[1];
        // }
    
        await this.$store.dispatch({
          type: "article/getAll",
          data: this.pagerequest,
        });
      }
      get pageSize() {
        return this.$store.state.article.pageSize;
      }
      get totalCount() {
        return this.$store.state.article.totalCount;
      }
      get currentPage() {
        return this.$store.state.article.currentPage;
      }
    
      columns = [
        {
          title: this.L("ArticleTitle"),
          key: "title",
        },
        {
          title: this.L("ArticleAuthor"),
          key: "author",
        },
        {
          title: this.L("IsActive"),
          render: (h: any, params: any) => {
            return h("span", params.row.isActive ? this.L("Yes") : this.L("No"));
          },
        },
        {
          title: this.L("Actions"),
          key: "Actions",
           150,
          render: (h: any, params: any) => {
            return h("div", [
              h(
                "Button",
                {
                  props: {
                    type: "primary",
                    size: "small",
                  },
                  style: {
                    marginRight: "5px",
                  },
                  on: {
                    click: () => {
                      this.$store.commit("article/edit", params.row);
                      this.$parent.currentOpt = "edit";
                      this.$parent.currentRouter = "edit";
                      this.$parent.currentId = params.row.id;
                    },
                  },
                },
                this.L("Edit")
              ),
              h(
                "Button",
                {
                  props: {
                    type: "error",
                    size: "small",
                  },
                  on: {
                    click: async () => {
                      this.$Modal.confirm({
                        title: this.L("Tips"),
                        content: this.L("DeleteUserConfirm"),
                        okText: this.L("Yes"),
                        cancelText: this.L("No"),
                        onOk: async () => {
                          await this.$store.dispatch({
                            type: "article/delete",
                            data: params.row,
                          });
                          await this.getpage();
                        },
                      });
                    },
                  },
                },
                this.L("Delete")
              ),
            ]);
          },
        },
      ];
      async created() {
        this.getpage();
      }
    }
    </script>
    View Code

    edit.vue使用ElementUI版本

    <template>
      <div class="app-container">
        <el-form
          :model="formModel"
          :rules="rules"
          ref="subForm"
          label-width="150px"
          class="form-container"
        >
          <div class="createPost-main-container">
            <!-- <el-row :gutter="rowGutter">
                <el-divider content-position="left">
                  <span style="color: rgba(41, 155, 255, 0.67);">{{L('ArticleDetails')}}</span>
                </el-divider>
              </el-row> -->
            <el-row :gutter="rowGutter">
              <el-col :span="8">
                <el-form-item
                  :label="L('ArticleTitle')"
                  prop="title"
                  class="postInfo-container-item"
                >
                  <el-input
                    :maxlength="32"
                    :minlength="2"
                    v-model="formModel.title"
                  />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item :label="L('ArticleType')" prop="articleType">
                  <el-select
                    size="mini"
                    v-model="formModel.articleType"
                    clearable
                    class="filter-item"
                  >
                    <el-option
                      v-for="item in articleTypeDataItems"
                      :key="item.id"
                      :label="item.name"
                      :value="item.id"
                    />
                  </el-select>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="rowGutter">
              <el-col :span="8">
                <el-form-item :label="L('ArticleCoverImg')" prop="coverImg">
                  <el-input v-model="formModel.coverImg" />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item>
                  <el-button type="primary" class="filter-item" size="mini">
                    <label for="file-upload">
                      {{ L("UploadFiles") }}
                      <input
                        type="file"
                        id="file-upload"
                        style="display: none"
                        accept=".png, .jpg, .jpeg"
                        @change="uploadFiles"
                      />
                    </label>
                  </el-button>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="rowGutter">
              <el-col :span="8">
                <el-form-item :label="L('ArticleAuthor')" prop="author">
                  <el-input :maxlength="32" v-model="formModel.author" />
                </el-form-item>
              </el-col>
              <el-col :span="8">
                <el-form-item prop="isActive">
                  <el-checkbox v-model="formModel.isActive">{{
                    L("IsActive")
                  }}</el-checkbox>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="rowGutter">
              <el-col :span="24">
                <el-form-item :label="L('ArticleContents')" prop="contents">
                  <quill-editor
                    v-model="formModel.contents"
                    ref="myQuillEditor"
                    style="height: 500px; margin-bottom: 30px"
                    :options="editorOption"
                  >
                    <!-- 自定义toolar -->
                    <div id="toolbar" slot="toolbar">
                      <!-- Add a bold button -->
                      <button class="ql-bold" title="加粗">Bold</button>
                      <button class="ql-italic" title="斜体">Italic</button>
                      <button class="ql-underline" title="下划线">underline</button>
                      <button class="ql-strike" title="删除线">strike</button>
                      <button class="ql-blockquote" title="引用"></button>
                      <button class="ql-code-block" title="代码"></button>
                      <button class="ql-header" value="1" title="标题1"></button>
                      <button class="ql-header" value="2" title="标题2"></button>
                      <!--Add list -->
                      <button
                        class="ql-list"
                        value="ordered"
                        title="有序列表"
                      ></button>
                      <button
                        class="ql-list"
                        value="bullet"
                        title="无序列表"
                      ></button>
                      <!-- Add font size dropdown -->
                      <select class="ql-header" title="段落格式">
                        <option selected>段落</option>
                        <option value="1">标题1</option>
                        <option value="2">标题2</option>
                        <option value="3">标题3</option>
                        <option value="4">标题4</option>
                        <option value="5">标题5</option>
                        <option value="6">标题6</option>
                      </select>
                      <select class="ql-size" title="字体大小">
                        <option value="10px">10px</option>
                        <option value="12px">12px</option>
                        <option value="14px">14px</option>
                        <option value="16px" selected>16px</option>
                        <option value="18px">18px</option>
                        <option value="20px">20px</option>
                      </select>
                      <select class="ql-font" title="字体">
                        <option value="SimSun">宋体</option>
                        <option value="SimHei">黑体</option>
                        <option value="Microsoft-YaHei">微软雅黑</option>
                        <option value="KaiTi">楷体</option>
                        <option value="FangSong">仿宋</option>
                        <option value="Arial">Arial</option>
                      </select>
                      <!-- Add subscript and superscript buttons -->
                      <select
                        class="ql-color"
                        value="color"
                        title="字体颜色"
                      ></select>
                      <select
                        class="ql-background"
                        value="background"
                        title="背景颜色"
                      ></select>
                      <select class="ql-align" value="align" title="对齐"></select>
                      <button class="ql-clean" title="还原"></button>
                      <!-- You can also add your own -->
                    </div>
                  </quill-editor>
                </el-form-item>
              </el-col>
            </el-row>
            <el-row :gutter="rowGutter">
              <el-col :span="8">
                <el-form-item>
                  <el-button @click="cancel">{{ L("Back") }}</el-button>
                  &nbsp;&nbsp;&nbsp;
                  <el-button @click="save" type="primary">{{ L("OK") }}</el-button>
                </el-form-item>
              </el-col>
            </el-row>
          </div>
        </el-form>
      </div>
    </template>
    <script lang="ts">
    import Axios from "axios";
    import { Quill, quillEditor } from "vue-quill-editor";
    import "quill/dist/quill.core.css";
    import "quill/dist/quill.snow.css";
    import "quill/dist/quill.bubble.css";
    //引入font.css
    import "@/assets/css/font.css";
    import appconst from "@/lib/appconst";
    
    // 自定义字体大小
    let Size = Quill.import("attributors/style/size");
    Size.whitelist = ["10px", "12px", "14px", "16px", "18px", "20px"];
    Quill.register(Size, true);
    
    // 自定义字体类型
    var fonts = [
      "SimSun",
      "SimHei",
      "Microsoft-YaHei",
      "KaiTi",
      "FangSong",
      "Arial",
      "Times-New-Roman",
      "sans-serif",
      "宋体",
      "黑体",
    ];
    var Font = Quill.import("formats/font");
    Font.whitelist = fonts;
    Quill.register(Font, true);
    
    import { ArticleTypeDataItems } from "@/lib/constData";
    import { Component, Vue, Inject, Prop, Watch } from "vue-property-decorator";
    import Util from "../../../lib/util";
    import AbpBase from "../../../lib/abpbase";
    import Article from "@/store/entities/article";
    
    @Component({
      components: { quillEditor },
    })
    export default class EditArticle extends AbpBase {
      @Prop({ type: Boolean, default: false }) value: boolean;
      activeName = "first";
      rowGutter = 30;
      formModel: Article = new Article();
      articleTypeDataItems = ArticleTypeDataItems;
      uploadFormat = [".jpg", ".png", ".jpeg"];
      editorOption = {
        placeholder: "请输入",
        theme: "snow", // or 'bubble'
        modules: {
          toolbar: {
            container: "#toolbar",
          },
        },
      };
      uploadFiles(e) {
        let file = e.target.files[0];
        /* eslint-disable no-undef */
        let param = new FormData(); // 创建form对象
        param.append("file", file); // 通过append向form对象添加数据
        // param.append("zoneId", zid);
        // console.log(param.get("file")); // FormData私有类对象,访问不到,可以通过get判断值是否传进去
        let config = {
          headers: { "Content-Type": "multipart/form-data" },
        };
        // 添加请求头
        Axios.post(
          appconst.remoteServiceBaseUrl + "/api/FileCommon/UploadFile",
          param,
          config
        ).then((res) => {
          this.formModel.coverImg = res.data.result.fileUrl;
        });
      }
      handleSuccess(res, file) {
        console.log(res.data);
        file.url = res.data.fileUrl;
        file.name = res.data.fileUrl;
        this.formModel.coverImg = res.data.fileUrl;
      }
      handleFormatError(file) {
        this.$Notice.warning({
          title: "The file format is incorrect",
          desc:
            "File format of " +
            file.name +
            " is incorrect, please select jpg or png.",
        });
      }
      handleMaxSize(file) {
        this.$Notice.warning({
          title: "Exceeding file size limit",
          desc: "File  " + file.name + " is too large, no more than 2M.",
        });
      }
      handleBeforeUpload() {
        //上传文件之前的逻辑处理,return false 阻止上传
        const check = false;
        if (!check) {
          this.$Notice.warning({
            title: "error,can't upload",
          });
        }
        return check;
      }
      created() {
        let editModel = null;
        if (this.$store.state.article.editArticle) {
          editModel = this.$store.state.article.editArticle;
        }
        this.formModel = Util.extend(true, {}, editModel);
      }
      // articleTypeChange(val: string) {
      //   this.formModel.articleType = parseInt(val);
      // }
      save() {
        (this.$refs.subForm as any).validate(async (valid: boolean) => {
          if (valid) {
            let typeName = "article/create";
            if (this && this.formModel && this.formModel.id) {
              typeName = "article/update";
            }
            await this.$store.dispatch({
              type: typeName,
              data: this.formModel,
            });
            (this.$refs.subForm as any).resetFields();
            this.$emit("save-success");
            this.$emit("input", false);
            this.$Notice.success({
              title: "tips",
              desc: "success",
            });
          }
        });
      }
      cancel() {
        this.$parent.currentOpt = "list";
        this.$parent.currentRouter = "list";
        this.$parent.currentId = 0;
      }
      rules = {
        title: [
          {
            required: true,
            message: this.L("FieldIsRequired", undefined, this.L("ArticleTitle")),
            trigger: "blur",
          },
        ],
        author: [
          {
            required: true,
            message: this.L("FieldIsRequired", undefined, this.L("ArticleAuthor")),
            trigger: "blur",
          },
        ],
        contents: [
          {
            required: true,
            message: this.L(
              "FieldIsRequired",
              undefined,
              this.L("ArticleContents")
            ),
            trigger: "blur",
          },
        ],
        articleType: [
          {
            required: true,
            message: this.L("FieldIsRequired", undefined, this.L("ArticleType")),
            trigger: "change",
            type: "number",
          },
        ],
      };
    }
    </script>
    View Code

    5、分页组件src》components》Pagination》index.vue:

    <template>
      <div :class="{ hidden: hidden }" class="pagination-container">
        <el-pagination
          :background="background"
          :current-page.sync="currentPage"
          :page-size.sync="pageSize"
          :layout="layout"
          :page-sizes="pageSizes"
          :total="total"
          v-bind="$attrs"
          @size-change="handleSizeChange"
          @current-change="handleCurrentChange"
        />
      </div>
    </template>
    
    <script>
    import { scrollTo } from "@/lib/scroll-to";
    
    export default {
      name: "Pagination",
      props: {
        total: {
          required: true,
          type: Number,
        },
        page: {
          type: Number,
          default: 1,
        },
        limit: {
          type: Number,
          default: 20,
        },
        pageSizes: {
          type: Array,
          default() {
            return [10, 20, 30, 50];
          },
        },
        layout: {
          type: String,
          default: "total, sizes, prev, pager, next, jumper",
        },
        background: {
          type: Boolean,
          default: true,
        },
        autoScroll: {
          type: Boolean,
          default: true,
        },
        hidden: {
          type: Boolean,
          default: false,
        },
      },
      computed: {
        currentPage: {
          get() {
            return this.page;
          },
          set(val) {
            this.$emit("update:page", val);
          },
        },
        pageSize: {
          get() {
            return this.limit;
          },
          set(val) {
            this.$emit("update:limit", val);
          },
        },
      },
      methods: {
        handleSizeChange(val) {
          this.$emit("pagination", this.currentPage, val);
          if (this.autoScroll) {
            scrollTo(0, 800);
          }
        },
        handleCurrentChange(val) {
          this.$emit("pagination", val, this.pageSize);
          if (this.autoScroll) {
            scrollTo(0, 800);
          }
        },
      },
    };
    </script>
    
    <style scoped>
    .pagination-container {
      background: #fff;
      padding: 0;
    }
    .pagination-container.hidden {
      display: none;
    }
    </style>
    View Code

    6、富文本编辑器我使用的是 npm install vue-quill-editor -save,参考文档:https://www.cnblogs.com/seven077/p/11313137.html

    7、使用ViewUI的Upload组件上传文件,这个类写在Framework.Web.Core项目中的Controller文件夹下,这个项目下面可以使用 HttpContext.Request.Form.Files 来获取上传的文件列表,

    如果写在Application这个项目里,方法要使用参数IFormFile,这个我没弄成功。

    using System;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.Mvc;
    using Abp.UI;
    using System.IO;
    using Framework.Common;
    using Microsoft.AspNetCore.Http;
    
    namespace Framework.Controllers
    {
        [Route("api/[controller]/[action]")]
        public class FileCommonController : FrameworkControllerBase
        {
            public FileCommonController() { }
            /// <summary>
            /// 上传一个文件,并返回文件上传成功后的信息
            /// </summary>
            /// <param name="file">要上传的文件实体</param>
            /// <returns>文件上传成功后返回的文件相关信息</returns>
            [HttpPost]
            public async Task<FileUploadOutputDto> UploadFile()
            {
                try
                {
                    var file = HttpContext.Request.Form.Files[0];
    
                    //文件的原始名称
                    string fileOriginName = file.FileName;
    
                    //读取文件保存的根目录
                    string fileSaveRootDir = Utils.GetAppSetting("App", "FileRootPath");
                    //读取办公管理文件保存的模块的根目录
                    //string fileSaveDir = Utils.GetAppSetting("App", "OAFiles");
                    //文件保存的相对文件夹(保存到wwwroot目录下)
                    string absoluteFileDir = fileSaveRootDir;
    
                    //文件保存的路径(应用的工作目录+文件夹相对路径);
                    string fileSavePath = Environment.CurrentDirectory + "/wwwroot/" + absoluteFileDir;
                    if (!Directory.Exists(fileSavePath))
                    {
                        Directory.CreateDirectory(fileSavePath);
                    }
    
                    //生成文件的名称
                    string extensionName = Path.GetExtension(fileOriginName);//获取文件的源后缀
                    if (string.IsNullOrEmpty(extensionName))
                    {
                        throw new UserFriendlyException("文件上传的原始名称好像不对哦,没有找到文件后缀");
                    }
                    string fileName = Guid.NewGuid().ToString() + extensionName;//通过uuid和原始后缀生成新的文件名
    
                    //最终生成的文件的相对路径(xxx/xxx/xx.xx)
                    string finalyFilePath = fileSavePath + "/" + fileName;
    
                    FileUploadOutputDto result = new FileUploadOutputDto();
    
                    //打开上传文件的输入流
                    using (Stream stream = file.OpenReadStream())
                    {
                        //创建输入流的reader
                        //var fileType = stream.GetFileType();
                        //文件大小
                        result.FileLength = stream.Length;
                        result.FileName = fileOriginName;
                        result.FileType = extensionName.Substring(1);
                        result.FileUrl = absoluteFileDir + "/" + fileName;
    
                        //开始保存拷贝文件
                        using (FileStream targetFileStream = new FileStream(finalyFilePath, FileMode.OpenOrCreate))
                        {
                            await stream.CopyToAsync(targetFileStream);
                        }
                    }
                    return result;
                }
                catch (Exception ex)
                {
                    throw new UserFriendlyException("文件上传失败,原因" + ex.Message);
                }
            }
        }
    }

    public class FileUploadOutputDto
    {
    public long FileLength { get; set; }
    public string FileName { get; set; }
    public string FileType { get; set; }
    public string FileUrl { get; set; }
    }

    Framework.Web.Host》appSettings.json加配置,或者自己随便写也行。

    using Abp.Localization;
    using Framework.Configuration;
    using Microsoft.Extensions.Configuration;
    using System;
    using System.IO;
    using System.Reflection;
    
    namespace Framework.Common
    {
        public class Utils
        {
            public static ILocalizableString L(string name)
            {
                return new LocalizableString(name, FrameworkConsts.LocalizationSourceName);
            }
            /// <summary>
            /// 获取物理路径
            /// </summary>
            /// <param name="seedFile">/floder1/floder2/</param>
            /// <returns></returns>
            public static string MapPath(string seedFile)
            {
                var absolutePath = new Uri(Assembly.GetExecutingAssembly().Location).AbsolutePath.Replace("/bin/Debug", "").Replace("net5.0", "");//CodeBase
                var directoryName = Path.GetDirectoryName(absolutePath);
                var path = directoryName + seedFile.Replace('/', '\');
                return path;
            }
    
            private static IConfigurationRoot _appConfiguration = AppConfigurations.Get(System.Environment.CurrentDirectory);
    
            //用法1(有嵌套):GetAppSetting("Authentication", "JwtBearer:SecurityKey")
            //用法2:GetAppSetting("App", "ServerRootAddress")
            public static string GetAppSetting(string section, string key)
            {
                return _appConfiguration.GetSection(section)[key];
            }
    
            public static string GetConnectionString(string key)
            {
                return _appConfiguration.GetConnectionString(key);
            }
    
        }
    }

    完了之后运行,接口地址就是 /api/FileCommon/UploadFile
    abp ViewUI使用Upload组件的代码,一些回调函数我没做,可以自己看着文档写一下。

    <Upload
                  name="coverImg"
                  ref="upload"
                  :show-upload-list="false"
                  :format="['jpg', 'jpeg', 'png']"
                  :max-size="2048"
                  multiple
                  type="drag"
                  action="http://localhost:21021/api/FileCommon/UploadFile"
                >
                  <Button icon="ios-cloud-upload-outline">{{
                    L("UploadFiles")
                  }}</Button>
                </Upload>

    这样就可以上传了,参考文章:建议都看一看
    http://v1.iviewui.com/components/upload
    https://www.cnblogs.com/yanan7890/p/12944523.html
    https://blog.csdn.net/war3ismylove/article/details/98201270
    https://blog.csdn.net/weixin_34114823/article/details/92453634

  • 相关阅读:
    javascript小白学习指南1---0
    C++学习笔记11-面向对象2
    cocos2d-x 3.0正式版 vs2013配置
    awk命令拷屏
    java实现第四届蓝桥杯空白格式化
    java实现第四届蓝桥杯空白格式化
    java实现第四届蓝桥杯空白格式化
    java实现第四届蓝桥杯空白格式化
    java实现第四届蓝桥杯空白格式化
    java实现第四届蓝桥杯危险系数
  • 原文地址:https://www.cnblogs.com/xsj1989/p/14411681.html
Copyright © 2011-2022 走看看