2 Komitmen bdccdf4150 ... e64525b74a

Pembuat SHA1 Pesan Tanggal
  chengmiaomiao@hoolihome.com e64525b74a 提交 4 tahun lalu
  chengmiaomiao@hoolihome.com fe42cb61a5 留学模块作者添加 4 tahun lalu

+ 6 - 1
src/common/api.js

@@ -255,7 +255,12 @@ const apis = {
   api_operation_material_category_info: `${api}/jcgi/operation/plat/material/category/info`, // 菜单详情
   api_operation_material_category_create: `${api}/jcgi/operation/plat/material/category/create`, // 菜单新建
   api_operation_material_category_update: `${api}/jcgi/operation/plat/material/category/update`, // 菜单编辑
-  api_operation_material_category_change_status: `${api}/jcgi/operation/plat/material/category/changeStatus` // 菜单状态上下架、删除
+  api_operation_material_category_change_status: `${api}/jcgi/operation/plat/material/category/changeStatus`, // 菜单状态上下架、删除
+  // 留学daily作者
+  api_operation_material_author_list: `${api}/jcgi/operation/plat/material/author/list`, // 列表
+  api_operation_material_author_info: `${api}/jcgi/operation/plat/material/author/info`, // 详情
+  api_operation_material_author_create: `${api}/jcgi/operation/plat/material/author/create`, // 新建
+  api_operation_material_author_update: `${api}/jcgi/operation/plat/material/author/update` // 编辑
 };
 
 export default apis;

+ 146 - 0
src/common/pages/daily4StudyAbroad/author.js

@@ -0,0 +1,146 @@
+// 留学daily话题表格列数组
+export const COLUMN_ARR = [
+  {
+    type: "index",
+    prop: "",
+    label: "",
+    width: "36",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "36",
+    "show-overflow-tooltip": true,
+    isFixed: true, // 列是否固定,无法移动
+    isVisible: true // 列是否显示,默认显示
+  },
+  {
+    type: "",
+    prop: "id",
+    label: "ID",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "80",
+    "show-overflow-tooltip": true,
+    isFixed: true,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "userName",
+    label: "昵称",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "160",
+    "show-overflow-tooltip": true,
+    isFixed: true,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "mobile",
+    label: "手机号",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "200",
+    "show-overflow-tooltip": true,
+    isFixed: false,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "materialNum",
+    label: "发布",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "200",
+    "show-overflow-tooltip": true,
+    isFixed: false,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "fansNum",
+    label: "粉丝",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "200",
+    "show-overflow-tooltip": true,
+    isFixed: false,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "likeNum",
+    label: "获赞",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "200",
+    "show-overflow-tooltip": true,
+    isFixed: false,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "updadteUserName",
+    label: "最后更新人",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "100",
+    "show-overflow-tooltip": true,
+    isFixed: false,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "updateTimeText",
+    label: "最后更新时间",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "160",
+    "show-overflow-tooltip": true,
+    isFixed: false,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "createUserName",
+    label: "创建人",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "100",
+    "show-overflow-tooltip": true,
+    isFixed: false,
+    isVisible: true
+  },
+  {
+    type: "",
+    prop: "createTimeText",
+    label: "创建时间",
+    width: "",
+    align: "center",
+    sortable: false,
+    "header-align": "center",
+    "min-width": "160",
+    "show-overflow-tooltip": true,
+    isFixed: false,
+    isVisible: true
+  }
+];

+ 1 - 1
src/common/pages/daily4StudyAbroad/material.js

@@ -34,7 +34,7 @@ export const COLUMN_ARR = [
     align: "center",
     sortable: false,
     "header-align": "center",
-    "min-width": "160",
+    "min-width": "250",
     "show-overflow-tooltip": true,
     isFixed: true,
     isVisible: true

+ 2 - 2
src/common/pages/daily4StudyAbroad/video.js

@@ -34,8 +34,8 @@ export const COLUMN_ARR = [
     align: "center",
     sortable: false,
     "header-align": "center",
-    "min-width": "160",
-    "show-overflow-tooltip": true,
+    "min-width": "250",
+    "show-overflow-tooltip": false,
     isFixed: true,
     isVisible: true
   },

+ 460 - 0
src/components/daily4StudyAbroad/author/CreateEdit.vue

@@ -0,0 +1,460 @@
+<template>
+  <el-drawer
+    size="100%"
+    direction="rtl"
+    width-class="w-100vw-100vw"
+    custom-class="create-edit-container"
+    element-loading-custom-class="hooli-loading"
+    element-loading-background="rgba(0, 0, 0, 0)"
+    :modal="false"
+    :show-close="false"
+    :visible.sync="isVisible"
+    :wrapperClosable="true"
+    :append-to-body="true"
+    @closed="handleClose"
+    v-loading="isShowLoading"
+  >
+    <div class="drawer-inner">
+      <div class="drawer-hd">
+        <div class="hd-l">
+          <h2>{{ currentId ? "编辑作者" : "新建作者" }}</h2>
+        </div>
+        <div class="hd-r">
+          <el-button
+            class="btn el-button-ff523d"
+            style="width: 80px;"
+            @click="handleClose()"
+            >取消</el-button
+          >
+          <el-button
+            class="btn el-button-ff523d active"
+            style="width: 80px;"
+            :loading="isSaveLock"
+            @click="handleSave"
+            >保存</el-button
+          >
+        </div>
+      </div>
+      <div class="drawer-bd-wrap">
+        <div class="drawer-bd">
+          <el-form
+            label-position="top"
+            class="create-edit-form"
+            :model="formData"
+            :rules="formRules"
+            ref="form"
+          >
+            <div class="form-item-wrap">
+              <el-form-item label="昵称" prop="userName" style="width: 100%;">
+                <el-input
+                  type="text"
+                  v-model="formData.userName"
+                  placeholder="请输入(2~10个字符)"
+                  maxlength="10"
+                  clearable
+                ></el-input>
+              </el-form-item>
+            </div>
+            <div class="form-item-wrap">
+              <el-form-item
+                label="个性签名"
+                prop="content"
+                style="width: 100%;"
+              >
+                <el-input
+                  type="textarea"
+                  v-model="formData.content"
+                  placeholder="请输入(最多50个字符)"
+                  :autosize="{ minRows: 2, maxRows: 4 }"
+                  maxlength="50"
+                  clearable
+                  show-word-limit
+                ></el-input>
+              </el-form-item>
+            </div>
+            <div class="form-item-wrap">
+              <el-form-item prop="avatar" style="width: 100%;">
+                <div class="tip-wrap">
+                  <p class="title">图片</p>
+                  <p class="tip">(仅上传一张)</p>
+                </div>
+                <el-button
+                  class="el-button-ff523d"
+                  icon="el-icon-plus"
+                  @click="isShowCoropper = true"
+                >
+                  上传</el-button
+                >
+                <div class="item" v-if="formData.avatar">
+                  <el-image
+                    :src="formData.avatar"
+                    :preview-src-list="[formData.avatar]"
+                    style="width: 84px; height: 84px;"
+                  ></el-image>
+                  <div
+                    class="colse"
+                    style="right: 20px;"
+                    v-on:click="formData.avatar = ''"
+                  ></div>
+                </div>
+              </el-form-item>
+            </div>
+          </el-form>
+          <div class="invisible">
+            <coropper-upload-file
+              v-if="isShowCoropper"
+              content-type="run/tagImg"
+              :proportion="1"
+              :limit-size-level="200"
+              tips="仅支持JPG、PNG等格式的图片,限制大小不超200K,宽高比1:1"
+              @onSuccess="handleCoropperSuccess"
+              @onCancel="isShowCoropper = false"
+            ></coropper-upload-file>
+          </div>
+        </div>
+      </div>
+    </div>
+  </el-drawer>
+</template>
+
+<script>
+import CoropperUploadFile from "@/components/common/CoropperImgUploadFile";
+
+export default {
+  components: { CoropperUploadFile },
+  props: {
+    // 编辑时传递的ID
+    currentId: {
+      type: [String, Number],
+      required: false,
+      default: ""
+    }
+  },
+  data() {
+    return {
+      isVisible: false, // 抽屉显隐
+      isShowLoading: false, // 编辑时请求详情loading
+      isSaveLock: false, // 点击保存时锁住请求
+      isShowCoropper: false, // 图片裁剪显示控制
+      // 对象
+      formData: {
+        userName: "", // 名称
+        content: "", // 详情
+        avatar: "" // 图片
+      },
+      // 表单校验规则
+      formRules: {
+        userName: [
+          { required: true, message: "请输入昵称名称", trigger: "blur" },
+          { min: 2, max: 10, message: "长度在 2 到 10 个字符", trigger: "blur" }
+        ]
+      }
+    };
+  },
+  mounted() {
+    this.isVisible = true;
+    this.loadDetail();
+  },
+  methods: {
+    // 取消
+    handleClose() {
+      this.$emit("closedEvent");
+      this.isVisible = false;
+    },
+    // 保存
+    handleSave() {
+      if (this.isSaveLock) {
+        return false;
+      }
+      // 保存操作
+      const doSave = () => {
+        const params = {};
+        if (this.currentId) {
+          params.url = this.$apis.api_operation_material_author_update;
+          params.data = {
+            authorId: this.currentId
+          };
+        } else {
+          params.url = this.$apis.api_operation_material_author_create;
+          params.data = {};
+        }
+        const formData = { ...this.formData };
+        Object.assign(params.data, formData);
+        params.data = this.$utils.removeEmptyProperty(params.data);
+        this.$net.req(params).then(
+          res => {
+            this.isSaveLock = false;
+            const { code } = res;
+            if (parseInt(code) === 0) {
+              this.$parent.$refs.detail &&
+                this.$parent.$refs.detail.loadDetail(true);
+              this.$parent.$refs.pageTableList &&
+                this.$parent.$refs.pageTableList.loadData(true);
+              if (!this.currentId) {
+                this.$message.success("创建成功");
+              } else {
+                this.$message.success("保存成功");
+              }
+              this.handleClose();
+              return false;
+            }
+            this.$message.error(
+              (res && res.msg) || "Oops, something seems went wrong~"
+            );
+          },
+          e => {
+            this.isSaveLock = false;
+            this.$message.error(
+              (e && e.msg) || "Oops, something seems went wrong~"
+            );
+          }
+        );
+        this.isSaveLock = true;
+      };
+      this.$refs.form.validate(valid => {
+        if (valid) {
+          doSave();
+        } else {
+          return false;
+        }
+      });
+    },
+    // 剪裁图片成功
+    handleCoropperSuccess(host, imgUri) {
+      this.formData.avatar = host + imgUri;
+    },
+    // 加载详情,isSlient=true静默加载,不显示loading
+    loadDetail(isSlient = false) {
+      if (!this.currentId) {
+        return false;
+      }
+      const params = {};
+      params.url = this.$apis.api_operation_material_author_info;
+      params.data = {
+        id: this.currentId
+      };
+      this.$net.req(params).then(
+        res => {
+          !isSlient && (this.isShowLoading = false);
+          const { data } = res;
+          if (data) {
+            this.adapterDetail(data);
+            return false;
+          }
+          this.$message.error((res && res.msg) || "获取详情数据失败");
+        },
+        e => {
+          !isSlient && (this.isShowLoading = false);
+          this.$message.error((e && e.msg) || "获取详情数据失败");
+        }
+      );
+      !isSlient && (this.isShowLoading = true);
+    },
+    // 详情适配器
+    adapterDetail(obj) {
+      const formData = {
+        userName: obj.userName || "",
+        content: obj.content || "",
+        avatar: obj.avatar || ""
+      };
+      this.formData = Object.assign(this.formData, formData);
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.create-edit-form {
+  display: flex;
+  flex-direction: column;
+  padding: 20px 0;
+  .form-item-wrap {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    .el-form-item {
+      margin-bottom: 8px;
+      margin-right: 10px;
+      &:last-child {
+        margin-right: 0;
+      }
+      .el-select {
+        width: 100%;
+      }
+      .select-button {
+        position: relative;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        width: 185px;
+        .el-select {
+          width: 153px;
+          /deep/ .el-input__inner {
+            border-top-right-radius: 0;
+            border-bottom-right-radius: 0;
+          }
+        }
+        .el-button {
+          width: 32px;
+          border-color: #e1e1e1;
+          padding: 8px 8px !important;
+          border-left: 0;
+          border-top-left-radius: 0;
+          border-bottom-left-radius: 0;
+          &[disabled] {
+            background-color: #f5f7fa !important;
+          }
+        }
+      }
+    }
+  }
+  .tip-wrap {
+    margin-top: 10px;
+    margin-bottom: 14px;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    .title {
+      font-size: 13px;
+      font-family: PingFangSC-Regular, PingFangSC;
+      font-weight: 400;
+      color: #666666;
+      line-height: 18px;
+      &.require::before {
+        content: "*";
+        color: #f56c6c;
+      }
+    }
+    .tip {
+      font-size: 13px;
+      font-family: PingFangSC-Regular, PingFangSC;
+      font-weight: 400;
+      color: #999999;
+      line-height: 18px;
+    }
+  }
+  .item {
+    position: relative;
+    width: 112px;
+    height: 84px;
+    margin: 14px 14px 14px 0;
+    .el-image {
+      width: 112px;
+      height: 84px;
+      cursor: grabbing;
+    }
+    .colse {
+      position: absolute;
+      width: 16px;
+      height: 16px;
+      background: #ff523d;
+      top: -8px;
+      right: -8px;
+      border-radius: 50%;
+      cursor: pointer;
+      &::before {
+        content: "";
+        position: absolute;
+        width: 8px;
+        height: 1px;
+        border-radius: 1px;
+        left: 50%;
+        top: 50%;
+        transform: translate(-50%, -50%);
+        background: #fff;
+      }
+    }
+  }
+  button[disabled] {
+    cursor: no-drop;
+    color: #fff;
+    border-color: #ff523d;
+    background-color: #ff523d;
+  }
+  .el-tag {
+    margin: 0 10px 10px 0;
+  }
+  .input-new-tag {
+    width: 150px;
+    margin-left: 0;
+  }
+  .invisible {
+    width: 0;
+    height: 0;
+    overflow: hidden;
+  }
+  .wraning {
+    color: #f56c6c;
+    font-size: 12px;
+    line-height: 1;
+    padding-top: 10px;
+    display: block;
+  }
+  /deep/ .el-radio__input.is-checked + .el-radio__label {
+    color: #ff523d;
+  }
+  /deep/.el-radio__input.is-checked .el-radio__inner {
+    border-color: #ff523d;
+    background: #ff523d;
+  }
+
+  .dialog {
+    position: fixed;
+    top: 0;
+    left: 0;
+    bottom: 0;
+    right: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 999;
+    background: #fff;
+    .close {
+      position: fixed;
+      right: 0;
+      width: 136px;
+      height: 50px;
+      top: 30px;
+      color: #333;
+      cursor: pointer;
+      background: rgba(0, 0, 0, 0.4);
+      border-radius: 25px 0 0 25px;
+      z-index: 2001;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 16px;
+      color: #ffffff;
+      i {
+        font-size: 22px;
+        margin-right: 5px;
+      }
+    }
+    .iframe-container {
+      margin: 0 auto;
+      #iframeM {
+        border: 0;
+      }
+      &.iframe-container-m {
+        width: 700px;
+        height: 130vh;
+        position: relative;
+        margin-top: 50px;
+        > img {
+          width: 700px;
+          position: absolute;
+          top: 0;
+        }
+        #iframeM {
+          width: 622px;
+          margin: 38px 37px 0;
+          height: 100vh;
+          padding-bottom: 88px;
+          border-top-left-radius: 60px;
+          border-top-right-radius: 60px;
+          position: relative;
+        }
+      }
+    }
+  }
+}
+</style>

+ 321 - 0
src/components/daily4StudyAbroad/author/Detail.vue

@@ -0,0 +1,321 @@
+<template>
+  <el-drawer
+    ref="drawer"
+    size="100%"
+    direction="rtl"
+    custom-class="drawer-detail-container"
+    element-loading-custom-class="hooli-loading"
+    element-loading-background="rgba(0, 0, 0, 0)"
+    v-loading="isLoading"
+    :modal="isShowModal"
+    :show-close="false"
+    :visible.sync="isVisible"
+    :wrapperClosable="true"
+    :append-to-body="true"
+    @closed="handleClose"
+    width-class="w-530px-530px"
+  >
+    <div v-if="detailObj" class="detail-drawer-wrap">
+      <div class="detail-drawer-header">
+        <div class="user-header">
+          <div class="info-wrap">
+            <span class="icon icon-daily_fill"></span>
+            <div class="info">
+              <p class="title twoline">{{ detailObj.tagName }}</p>
+            </div>
+            <i class="close el-icon-close" @click="handleClose"></i>
+          </div>
+        </div>
+        <div class="operating">
+          <div class="btns-wrap">
+            <el-button class="btn el-button-f9f9f9" @click="handleEdit"
+              >编辑</el-button
+            >
+          </div>
+          <i class="refresh icon icon-shuaxin" @click="handleRefresh"></i>
+        </div>
+      </div>
+      <div class="tabs-wrap">
+        <el-tabs v-model="activeName">
+          <el-tab-pane label="详细信息" name="first">
+            <section class="project-detail-info-container">
+              <p class="caption">基本信息</p>
+              <div class="field-items">
+                <div class="item w-100">
+                  <div class="key w-65">昵称</div>
+                  <div class="value">
+                    {{ detailObj.userName }}
+                  </div>
+                </div>
+                <div class="item w-100">
+                  <div class="key w-65">签名</div>
+                  <div class="value">
+                    {{ detailObj.content || "--" }}
+                  </div>
+                </div>
+                <div class="item w-100">
+                  <div class="key w-65">头像</div>
+                  <div class="value img-list" v-if="detailObj.avatar">
+                    <el-image
+                      object-fit="cover"
+                      class="img"
+                      style="width: 84px; height: 84px;"
+                      :src="detailObj.avatar"
+                      :preview-src-list="[detailObj.avatar]"
+                    ></el-image>
+                  </div>
+                  <div class="value" v-else>--</div>
+                </div>
+                <div class="item w-100">
+                  <div class="key w-65">发布</div>
+                  <div class="value">
+                    {{ detailObj.materialNum }}
+                  </div>
+                </div>
+                <div class="item w-100">
+                  <div class="key w-65">粉丝</div>
+                  <div class="value">
+                    {{ detailObj.fansNum }}
+                  </div>
+                </div>
+                <div class="item w-100">
+                  <div class="key w-65">获赞</div>
+                  <div class="value">
+                    {{ detailObj.likeNum }}
+                  </div>
+                </div>
+                <div class="item w-100">
+                  <div class="key w-65">参与人数</div>
+                  <div class="value">
+                    {{ detailObj.participationNumber }}
+                  </div>
+                </div>
+              </div>
+              <p class="caption">系统维护信息</p>
+              <div class="field-items">
+                <div class="item">
+                  <div class="key w-80">创建人</div>
+                  <div class="value">
+                    {{
+                      detailObj.createUserName ? detailObj.createUserName : "--"
+                    }}
+                  </div>
+                </div>
+                <div class="item">
+                  <div class="key w-80">创建时间</div>
+                  <div class="value">
+                    {{
+                      detailObj.createTimeText ? detailObj.createTimeText : "--"
+                    }}
+                  </div>
+                </div>
+                <div class="item">
+                  <div class="key w-80">最后更新人</div>
+                  <div class="value">
+                    {{
+                      detailObj.updadteUserName
+                        ? detailObj.updadteUserName
+                        : "--"
+                    }}
+                  </div>
+                </div>
+                <div class="item">
+                  <div class="key w-80">最后更新时间</div>
+                  <div class="value">
+                    {{
+                      detailObj.updateTimeText ? detailObj.updateTimeText : "--"
+                    }}
+                  </div>
+                </div>
+              </div>
+            </section>
+          </el-tab-pane>
+        </el-tabs>
+      </div>
+    </div>
+  </el-drawer>
+</template>
+
+<script>
+export default {
+  props: {
+    // 当前显示的ID
+    currentId: {
+      type: [String, Number],
+      required: true
+    }
+  },
+  data() {
+    return {
+      isVisible: false,
+      isShowModal: false,
+      isLoading: false,
+      detailObj: null, // 详情对象
+      activeName: "first"
+    };
+  },
+  watch: {
+    currentId: {
+      immediate: true,
+      handler() {
+        this.loadDetail();
+      }
+    }
+  },
+  mounted() {
+    this.isVisible = true;
+  },
+  methods: {
+    // 刷新表格数据
+    tableRefresh() {
+      const pageTableList = this.$parent.$refs.pageTableList;
+      if (pageTableList) {
+        pageTableList.loadData(true);
+      }
+    },
+    // 点击关闭抽屉
+    handleClose() {
+      this.$emit("closedEvent");
+      this.isVisible = false;
+    },
+    // 点击编辑
+    handleEdit() {
+      this.$parent.loadDrawer("createEdit", this.currentId);
+    },
+    // 点击刷新
+    handleRefresh() {
+      this.loadDetail();
+    },
+    // 加载详情,isSlient=true静默加载,不显示loading
+    loadDetail(isSlient = false) {
+      if (!this.currentId) {
+        this.isLoading = false;
+        this.detailObj = null;
+        return false;
+      }
+      const params = {};
+      params.url = this.$apis.api_operation_material_author_info;
+      params.data = {
+        id: this.currentId
+      };
+      this.$net.req(params).then(
+        res => {
+          !isSlient && (this.isLoading = false);
+          const { data } = res;
+          if (data) {
+            this.detailObj = data;
+            return false;
+          }
+          this.detailObj = null;
+        },
+        () => {
+          !isSlient && (this.isLoading = false);
+          this.detailObj = null;
+        }
+      );
+      !isSlient && (this.isLoading = true);
+      this.detailObj = null;
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.project-detail-info-container {
+  width: 100%;
+  height: auto;
+  padding: 20px;
+  overflow-y: auto;
+  .caption {
+    position: relative;
+    font-size: 14px;
+    font-family: PingFangSC-Regular, PingFangSC;
+    font-weight: 400;
+    color: rgba(26, 26, 26, 1);
+    line-height: 20px;
+    padding-left: 8px;
+    &::before {
+      content: "";
+      position: absolute;
+      width: 2px;
+      height: 12px;
+      background: #ff523d;
+      border-radius: 1px;
+      left: 0;
+      top: 50%;
+      transform: translateY(-50%);
+    }
+  }
+  .field-items {
+    display: flex;
+    flex-wrap: wrap;
+    margin-bottom: 20px;
+    .item {
+      display: flex;
+      margin-top: 14px;
+      width: 50%;
+      &.w-100 {
+        width: 100%;
+      }
+      &.content {
+        .value {
+          width: 400px;
+          /deep/ img {
+            max-width: 400px;
+          }
+        }
+      }
+      .key {
+        font-size: 13px;
+        font-family: PingFangSC-Regular, PingFangSC;
+        font-weight: 400;
+        color: #999999;
+        line-height: 18px;
+        width: 91px;
+        margin-right: 14px;
+        text-align: right;
+        flex-shrink: 0;
+        &.w-65 {
+          width: 65px;
+        }
+        &.w-70 {
+          width: 70px;
+        }
+      }
+      .value {
+        font-size: 13px;
+        font-family: PingFangSC-Regular, PingFangSC;
+        font-weight: 400;
+        color: #1a1a1a;
+        line-height: 18px;
+        word-break: break-all;
+        word-wrap: break-word;
+        .icon-hooli-copy {
+          margin-left: 10px;
+          color: #666666;
+          cursor: pointer;
+        }
+      }
+    }
+    .el-image {
+      width: 60px;
+      height: 45px;
+      &.img {
+        margin-right: 10px;
+      }
+      &.img-more {
+        width: 52px;
+      }
+    }
+  }
+}
+.play {
+  position: absolute;
+  left: 50%;
+  top: 50%;
+  transform: translate(-50%, -50%);
+  font-size: 30px;
+  color: #fff;
+  cursor: pointer;
+}
+</style>

+ 138 - 0
src/components/daily4StudyAbroad/author/Page.vue

@@ -0,0 +1,138 @@
+<template>
+  <section class="page-container">
+    <template v-if="pageState === 1">
+      <div
+        v-loading="true"
+        class="page-loading"
+        element-loading-custom-class="hooli-loading"
+      ></div>
+    </template>
+    <template v-else-if="pageState === 2">
+      <page-header ref="pageHeader"></page-header>
+      <page-table-list
+        ref="pageTableList"
+        :column-arr="columnArr"
+      ></page-table-list>
+      <page-pagination ref="pagePagination"></page-pagination>
+      <section class="page-other">
+        <detail
+          v-if="isLoadDetail"
+          ref="detail"
+          :current-id="currentId"
+          @closedEvent="isLoadDetail = false"
+        ></detail>
+        <create-edit
+          v-if="isLoadCreateEdit"
+          ref="createEdit"
+          :current-id="currentId"
+          @closedEvent="isLoadCreateEdit = false"
+        ></create-edit>
+      </section>
+    </template>
+    <template v-else-if="pageState === 3">
+      <div class="page-wrong">
+        抱歉,貌似出了点错,
+        <br />
+        <br />请检查网络刷新重试或联系技术人员咨询
+      </div>
+    </template>
+  </section>
+</template>
+
+<script>
+import PageHeader from "@/components/daily4StudyAbroad/author/PageHeader";
+import PageTableList from "@/components/daily4StudyAbroad/author/PageTableList";
+import PagePagination from "@/components/daily4StudyAbroad/author/PagePagination";
+import Detail from "@/components/daily4StudyAbroad/author/Detail";
+import CreateEdit from "@/components/daily4StudyAbroad/author/CreateEdit";
+
+import { COLUMN_ARR } from "@/common/pages/daily4StudyAbroad/author.js";
+
+export default {
+  components: {
+    PageHeader,
+    PageTableList,
+    PagePagination,
+    Detail,
+    CreateEdit
+  },
+  data() {
+    return {
+      pageState: 1, // page状态,默认1=loading,2=ok,3=wrong
+      columnArr: [], // 表格所有列数组
+      isLoadDetail: false, // 是否加载详情组件
+      isLoadCreateEdit: false, // 是否加载新建编辑组件
+      currentId: "" // 当前正在显示或编辑的ID
+    };
+  },
+  mounted() {
+    this.domSystemLayoutContainer = document.querySelector(
+      ".system-layout-container"
+    );
+    this.layerClickHandler = () => {
+      this.unloadDrawer("detail");
+    };
+    this.domSystemLayoutContainer.addEventListener(
+      "click",
+      this.layerClickHandler,
+      false
+    );
+    window.setTimeout(() => {
+      this.columnArr = COLUMN_ARR;
+      this.pageState = 2;
+      this.$nextTick(() => {
+        this.$refs.pageTableList.loadData();
+      });
+    }, 300);
+  },
+  destoryed() {
+    this.domSystemLayoutContainer.removeEventListener(
+      "click",
+      this.layerClickHandler
+    );
+    this.layerClickHandler = null;
+  },
+  methods: {
+    // 加载 详情、新建编辑 组件
+    loadDrawer(what, id = "") {
+      switch (what) {
+        case "detail": {
+          this.currentId = id;
+          this.isLoadDetail = true;
+          break;
+        }
+        case "createEdit": {
+          this.currentId = id;
+          this.isLoadCreateEdit = true;
+          break;
+        }
+        default: {
+          break;
+        }
+      }
+    },
+    // 卸载 详情、新建编辑 抽屉组件
+    unloadDrawer(what, isNeedRefresh) {
+      switch (what) {
+        case "detail": {
+          this.currentId = "";
+          this.$refs.detail && this.$refs.detail.$refs.drawer.closeDrawer();
+          break;
+        }
+        case "createEdit": {
+          if (isNeedRefresh) {
+            this.$refs.detail.loadDetail();
+            this.$refs.pageTableList.loadData(true);
+          }
+          this.$refs.createEdit &&
+            this.$refs.createEdit.$refs.drawer.closeDrawer();
+          break;
+        }
+        default: {
+          break;
+        }
+      }
+    }
+  }
+};
+</script>

+ 384 - 0
src/components/daily4StudyAbroad/author/PageHeader.vue

@@ -0,0 +1,384 @@
+<template>
+  <section class="page-hd">
+    <div class="main-action">
+      <div class="main-left">
+        <h2 class="title">昵称</h2>
+        <div class="search">
+          <el-input
+            class="search-ipt"
+            placeholder="请输入昵称搜索"
+            size="small"
+            clearable
+            v-model="filterObj.userName"
+            @clear="handleClearSearch"
+            @keyup.enter.native="handleFilter"
+          >
+            <template slot="append">
+              <el-button
+                class="icon"
+                icon="el-icon-search"
+                @click="handleFilter"
+              ></el-button>
+            </template>
+          </el-input>
+        </div>
+      </div>
+      <div class="main-right">
+        <div class="btns">
+          <el-button
+            class="icon el-button-ff523d"
+            icon="icon-hooli-add"
+            style="width: 80px;"
+            @click="handleCreate"
+            >新建</el-button
+          >
+        </div>
+      </div>
+    </div>
+    <!-- <div class="sub-action">
+      <ul class="filters">
+        <li class="filter-each status">
+          <p class="label">状态</p>
+          <el-select
+            v-model="filterObj.status"
+            clearable
+            size="mini"
+            @change="handleFilter"
+          >
+            <el-option
+              v-for="(item, index) in optionsObj.statusOptions"
+              :key="index"
+              :value="item.value"
+              :label="item.name"
+            ></el-option>
+          </el-select>
+        </li>
+        <li class="filter-each filter-advance">
+          <el-button
+            size="mini"
+            icon="icon-hooli-screening"
+            class="icon el-button-f9f9f9"
+            style="width: 100px; padding: 6px 15px;"
+            @click="handleShowAdvanceFilter(true)"
+            >高级筛选</el-button
+          >
+        </li>
+        <li class="filter-each clear-filter">
+          <div @click="handleClearFilters">
+            <span>清空筛选</span>
+          </div>
+        </li>
+      </ul>
+      <ul class="filters">
+        <li class="filter-each refresh">
+          <el-button
+            type="text"
+            class="icon"
+            icon="el-icon-refresh-right"
+            style="padding: 7px 0;font-size: 16px;color: #666;"
+            @click="handleFilter"
+          ></el-button>
+        </li>
+      </ul>
+    </div> -->
+    <div class="filters-result" v-if="advanceFilterArr.length > 0">
+      <el-tag
+        v-for="(val, index) in advanceFilterArr"
+        class="tag"
+        size="mini"
+        :key="index"
+        closable
+        :disable-transitions="false"
+        @close="handleRemoveTag(val.key)"
+      >
+        <template>{{ val.text }}</template>
+      </el-tag>
+    </div>
+    <div class="other-invisible">
+      <page-header-filters
+        v-if="isShowAdvanceFilters"
+        ref="advanceFilters"
+        :advance-filters="advanceFilterObj"
+      ></page-header-filters>
+    </div>
+  </section>
+</template>
+
+<script>
+import PageHeaderFilters from "@/components/daily4StudyAbroad/author/PageHeaderFilters";
+
+// const FILTERS = {
+//   userName: ""
+//   // tagName: "", // 名称
+//   // id: "", // ID
+//   // status: "" // 状态
+// };
+const ADVANCE_FILTERS = {
+  createUserId: "", // 创建人ID
+  updateUserId: "", // 更新人ID
+  createTimeStart: "", // 创建时间起始10位时间戳
+  createTimeEnd: "", // 创建时间结束10位时间戳
+  updateTimeStart: "", // 更新时间起始10位时间戳
+  updateTimeEnd: "" // 更新时间结束10位时间戳
+};
+
+export default {
+  components: { PageHeaderFilters },
+  data() {
+    return {
+      // 所有筛选选项对象
+      optionsObj: {
+        searchTypes: [
+          {
+            id: 1,
+            name: "名称",
+            placeholder: "请输入名称搜索"
+          },
+          {
+            id: 2,
+            name: "ID",
+            placeholder: "请输入ID搜索"
+          }
+        ], // 搜索类型
+        statusOptions: [
+          {
+            name: "上架",
+            value: 1
+          },
+          {
+            name: "下架",
+            value: 2
+          }
+        ] // 状态选项
+      },
+      searchTypeId: 1, // 选择搜索类型ID
+      searchText: "", // 搜索文本
+      filterObj: { userName: "" }, // 筛选条件对象
+      advanceFilterObj: { ...ADVANCE_FILTERS }, // 高级筛选条件对象
+      advanceFilterArr: [], // 高级筛选条件数组
+      isShowAdvanceFilters: false // 显隐高级筛选
+    };
+  },
+  computed: {
+    // 当前选中的搜索类型
+    selectSearchType() {
+      return this.optionsObj.searchTypes.filter(val => {
+        return val.id === this.searchTypeId;
+      })[0];
+    }
+  },
+  watch: {
+    searchTypeId() {
+      this.toggleSearch();
+    },
+    searchText() {
+      this.toggleSearch();
+    }
+  },
+  methods: {
+    toggleSearch() {
+      switch (this.selectSearchType.id) {
+        // 名称
+        case 1: {
+          this.filterObj.tagName = this.searchText.trim();
+          this.filterObj.id = "";
+          break;
+        }
+        // ID
+        case 2: {
+          this.filterObj.tagName = "";
+          this.filterObj.id = this.searchText.trim();
+          break;
+        }
+        default: {
+          this.filterObj.tagName = "";
+          this.filterObj.id = "";
+          break;
+        }
+      }
+    },
+    // set高级筛选数据
+    setAdvanceFilterObj(advanceFilterObj) {
+      this.advanceFilterObj = { ...advanceFilterObj };
+      this.handleFilter();
+    }, // set高级筛选数据
+    setAdvanceFilterArr(advanceFilterArr) {
+      this.advanceFilterArr = [...advanceFilterArr];
+    },
+    // 往父组件暴露筛选条件对象filterObj
+    getFilterObj() {
+      const filterObj = { ...this.filterObj, ...this.advanceFilterObj };
+      if (filterObj.createTimeStart) {
+        filterObj.createTimeStart = parseInt(filterObj.createTimeStart / 1000);
+      }
+      if (filterObj.createTimeEnd) {
+        filterObj.createTimeEnd = parseInt(filterObj.createTimeEnd / 1000);
+      }
+      if (filterObj.updateTimeStart) {
+        filterObj.updateTimeStart = parseInt(filterObj.updateTimeStart / 1000);
+      }
+      if (filterObj.updateTimeEnd) {
+        filterObj.updateTimeEnd = parseInt(filterObj.updateTimeEnd / 1000);
+      }
+      return JSON.parse(JSON.stringify(filterObj));
+    },
+    handleClearSearch() {
+      this.toggleSearch();
+      this.handleFilter();
+    },
+    handleShowAdvanceFilter(flag) {
+      this.isShowAdvanceFilters = flag;
+    },
+    handleRemoveTag(key) {
+      if (key === "createTime") {
+        this.advanceFilterObj.createTimeStart = "";
+        this.advanceFilterObj.createTimeEnd = "";
+      } else if (key === "updateTime") {
+        this.advanceFilterObj.updateTimeStart = "";
+        this.advanceFilterObj.updateTimeEnd = "";
+      } else {
+        this.advanceFilterObj[key] = "";
+      }
+      this.advanceFilterArr = this.advanceFilterArr.filter(val => {
+        return val.key !== key;
+      });
+      this.handleFilter();
+    },
+    // 重置筛选条件
+    handleClearFilters() {
+      this.searchText = "";
+      // this.filterObj = { ...FILTERS };
+      this.advanceFilterObj = { ...ADVANCE_FILTERS };
+      this.advanceFilterArr = [];
+      this.handleFilter();
+    },
+    // 列表刷新点击,筛选项目名称、项目类型、项目所属触发事件
+    handleFilter() {
+      const pagePagination = this.$parent.$refs.pagePagination;
+      if (pagePagination) {
+        pagePagination.setPage(1);
+      }
+      const pageTableList = this.$parent.$refs.pageTableList;
+      if (pageTableList) {
+        pageTableList.loadData();
+      }
+    },
+    // 新建点击
+    handleCreate() {
+      this.$parent.loadDrawer("createEdit");
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.page-hd {
+  padding: 20px 14px 14px 14px;
+  .tag {
+    margin: 10px 10px 0 0;
+  }
+  .main-action {
+    margin-bottom: 12px;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    overflow: hidden;
+    .main-left {
+      display: flex;
+      align-items: center;
+      .title {
+        font-size: 18px;
+        height: 25px;
+        line-height: 25px;
+        color: #1a1a1a;
+        font-weight: 500;
+        margin-right: 30px;
+        padding-left: 6px;
+      }
+      .search {
+        .search-ipt {
+          width: 400px;
+          .landlord-search-select {
+            width: 96px;
+            color: #666666;
+          }
+        }
+      }
+    }
+    .main-right {
+      .btns {
+        display: flex;
+        align-items: center;
+        .btns-item {
+          padding: 7px 14px;
+          background: #ff523d;
+          color: #fff;
+          font-size: 13px;
+          line-height: 18px;
+          border-radius: 3px;
+          margin-left: 10px;
+          & > i {
+            margin-right: 5px;
+          }
+        }
+      }
+    }
+  }
+  .sub-action {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    .filters {
+      display: flex;
+      align-items: center;
+      .filter-each {
+        display: flex;
+        align-items: center;
+        &:not(:last-child) {
+          margin-right: 20px;
+        }
+        &.status {
+          .el-select {
+            width: 100px;
+          }
+        }
+        &.author {
+          .el-select {
+            width: 140px;
+          }
+        }
+        &.clear-filter {
+          color: #007afe;
+          font-size: 13px;
+          text-decoration: underline;
+          cursor: pointer;
+        }
+        &.refresh {
+          cursor: pointer;
+          margin-left: -6px;
+        }
+        .label {
+          font-size: 13px;
+          color: #666666;
+          margin-right: 7px;
+        }
+        .column-settings-btn {
+          border-radius: 3px;
+          border: 1px solid #e1e1e1;
+          font-size: 13px;
+          font-weight: 400;
+          color: #666666;
+          padding: 8px 14px;
+          .icon {
+            margin-right: 5px;
+          }
+        }
+      }
+    }
+  }
+  .other-invisible {
+    width: 0;
+    height: 0;
+  }
+}
+</style>

+ 486 - 0
src/components/daily4StudyAbroad/author/PageHeaderFilters.vue

@@ -0,0 +1,486 @@
+<template>
+  <el-dialog
+    class="filter-dialog"
+    :visible.sync="isVisible"
+    :close-on-click-modal="false"
+    :close-on-press-escape="false"
+    :show-close="false"
+    :append-to-body="true"
+    width="500px"
+    :before-close="handleCancel"
+  >
+    <div slot="title" class="dialog-title">
+      <div class="top">
+        <h2>高级筛选</h2>
+        <i class="el-icon-close" @click="handleCancel"></i>
+      </div>
+    </div>
+    <div class="dialog-bd">
+      <div class="filter-wrap">
+        <div class="filter-item" style="width:140px">
+          <div class="filter-title">创建人</div>
+          <div class="filter-info">
+            <div class="filter-info-item" style="width:90px;">
+              <el-select
+                v-model="advanceFilterObj.createUserId"
+                placeholder="请选择"
+                size="mini"
+                filterable
+                clearable
+                @change="handleFilter"
+              >
+                <el-option
+                  v-for="(item, index) in optionsObj.userList"
+                  :key="index"
+                  :value="item.value"
+                  :label="item.name"
+                ></el-option>
+              </el-select>
+            </div>
+          </div>
+        </div>
+        <div class="filter-item" style="width:292px;">
+          <div class="filter-title">创建时间</div>
+          <div class="filter-info">
+            <div class="filter-info-item">
+              <el-date-picker
+                :picker-options="createTimeStartPickerOptions"
+                v-model="advanceFilterObj.createTimeStart"
+                @change="handleFilter"
+                size="mini"
+                style="width:136px"
+                type="date"
+                format="yyyy/MM/dd"
+                value-format="timestamp"
+                placeholder="请选择"
+              />
+            </div>
+            <span class="line">~</span>
+            <div class="filter-info-item">
+              <el-date-picker
+                :picker-options="createTimeEndPickerOptions"
+                v-model="advanceFilterObj.createTimeEnd"
+                @change="handleFilter"
+                size="mini"
+                style="width:136px"
+                type="date"
+                format="yyyy/MM/dd"
+                value-format="timestamp"
+                placeholder="请选择"
+              />
+            </div>
+          </div>
+        </div>
+        <div class="filter-item" style="width:140px;">
+          <div class="filter-title">最后更新人</div>
+          <div class="filter-info">
+            <div class="filter-info-item" style="width:90px;">
+              <el-select
+                v-model="advanceFilterObj.updateUserId"
+                placeholder="请选择"
+                size="mini"
+                filterable
+                clearable
+                @change="handleFilter"
+              >
+                <el-option
+                  v-for="(item, index) in optionsObj.userList"
+                  :key="index"
+                  :value="item.value"
+                  :label="item.name"
+                ></el-option>
+              </el-select>
+            </div>
+          </div>
+        </div>
+        <div class="filter-item" style="width:292px;">
+          <div class="filter-title">最后更新时间</div>
+          <div class="filter-info">
+            <div class="filter-info-item">
+              <el-date-picker
+                :picker-options="updateTimeStartPickerOptions"
+                v-model="advanceFilterObj.updateTimeStart"
+                @change="handleFilter"
+                size="mini"
+                style="width:136px"
+                type="date"
+                format="yyyy/MM/dd"
+                value-format="timestamp"
+                placeholder="请选择"
+              />
+            </div>
+            <span class="line">~</span>
+            <div class="filter-info-item">
+              <el-date-picker
+                :picker-options="updateTimeEndPickerOptions"
+                v-model="advanceFilterObj.updateTimeEnd"
+                @change="handleFilter"
+                size="mini"
+                style="width:136px"
+                type="date"
+                format="yyyy/MM/dd"
+                value-format="timestamp"
+                placeholder="请选择"
+              />
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+    <div slot="footer" class="dialog-footer">
+      <div class="tips">
+        <span v-if="!isOk">
+          <i class="icon el-icon-warning-outline"></i> 请选择筛选条件
+        </span>
+      </div>
+      <div class="footer-wrap">
+        <el-button class="btn el-button-fff" @click="handleClear"
+          >清空</el-button
+        >
+        <el-button class="btn el-button-ff523d active" @click="handleSave"
+          >确定</el-button
+        >
+      </div>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+const ADVANCE_FILTERS = {
+  createUserId: "", // 创建人ID
+  updateUserId: "", // 更新人ID
+  createTimeStart: "", // 创建时间起始10位时间戳
+  createTimeEnd: "", // 创建时间结束10位时间戳
+  updateTimeStart: "", // 更新时间起始10位时间戳
+  updateTimeEnd: "" // 更新时间结束10位时间戳
+};
+
+export default {
+  props: {
+    advanceFilters: {
+      type: Object,
+      required: false,
+      default: () => {}
+    }
+  },
+  data() {
+    return {
+      isVisible: false, // dialog显隐
+      // 所有筛选选项对象
+      optionsObj: {
+        userList: [] // 创建人、更新人列表
+      },
+      advanceFilterObj: { ...ADVANCE_FILTERS }, // 高级筛选条件对象
+      advanceFilterArr: [], // 高级筛选条件数组
+      isOk: true, // 是否有选择筛选条件
+      createTimeStartPickerOptions: {
+        disabledDate: time => {
+          if (this.advanceFilterObj.createTimeEnd) {
+            return time.getTime() >= this.advanceFilterObj.createTimeEnd;
+          }
+        }
+      },
+      createTimeEndPickerOptions: {
+        disabledDate: time => {
+          if (this.advanceFilterObj.createTimeStart) {
+            return time.getTime() <= this.advanceFilterObj.createTimeStart;
+          }
+        }
+      },
+      updateTimeStartPickerOptions: {
+        disabledDate: time => {
+          if (this.advanceFilterObj.updateTimeEnd) {
+            return time.getTime() >= this.advanceFilterObj.updateTimeEnd;
+          }
+        }
+      },
+      updateTimeEndPickerOptions: {
+        disabledDate: time => {
+          if (this.advanceFilterObj.updateTimeStart) {
+            return time.getTime() <= this.advanceFilterObj.updateTimeStart;
+          }
+        }
+      }
+    };
+  },
+  watch: {
+    advanceFilterObj: {
+      deep: true,
+      handler() {
+        this.$nextTick(() => {
+          const isOk = this.getIsOk();
+          isOk && (this.isOk = true);
+
+          this.dealFilterArr();
+        });
+      }
+    }
+  },
+  created() {
+    this.loadUser();
+  },
+  mounted() {
+    this.isVisible = true;
+  },
+  methods: {
+    formatTimeToStr(t) {
+      if (t) {
+        let time = new Date(t);
+        let year = time.getFullYear();
+        let month = time.getMonth() + 1;
+        if (month < 10) {
+          month = `0${month}`;
+        }
+        let date = time.getDate();
+        if (date < 10) {
+          date = `0${date}`;
+        }
+        return `${year}/${month}/${date}`;
+      }
+      return "";
+    },
+    getIsOk() {
+      let isOk = false;
+      for (const key in this.advanceFilterObj) {
+        if (this.advanceFilterObj[key]) {
+          isOk = true;
+          break;
+        }
+      }
+      return isOk;
+    },
+    dealFilterArr() {
+      const advanceFilterArr = [];
+      if (this.advanceFilterObj.createUserId) {
+        const user = this.optionsObj.userList.filter(val => {
+          return val.value === this.advanceFilterObj.createUserId;
+        })[0];
+        user &&
+          advanceFilterArr.push({
+            key: "createUserId",
+            text: `创建人:${user.name}`
+          });
+      }
+      let start = !!this.advanceFilterObj.createTimeStart;
+      let end = !!this.advanceFilterObj.createTimeEnd;
+      if (start || end) {
+        if (start && !end) {
+          advanceFilterArr.push({
+            key: "createTimeStart",
+            text: `创建时间:>= ${this.formatTimeToStr(
+              this.advanceFilterObj.createTimeStart
+            )}`
+          });
+        } else if (!start && end) {
+          advanceFilterArr.push({
+            key: "createTimeEnd",
+            text: `创建时间:<= ${this.formatTimeToStr(
+              this.advanceFilterObj.createTimeEnd
+            )}`
+          });
+        } else if (start && end) {
+          advanceFilterArr.push({
+            key: "createTime",
+            text: `创建时间:${this.formatTimeToStr(
+              this.advanceFilterObj.createTimeStart
+            )} ~ ${this.formatTimeToStr(this.advanceFilterObj.createTimeEnd)}`
+          });
+        }
+      }
+      if (this.advanceFilterObj.updateUserId) {
+        const user = this.optionsObj.userList.filter(val => {
+          return val.value === this.advanceFilterObj.updateUserId;
+        })[0];
+        user &&
+          advanceFilterArr.push({
+            key: "updateUserId",
+            text: `最后更新人:${user.name}`
+          });
+      }
+      start = !!this.advanceFilterObj.updateTimeStart;
+      end = !!this.advanceFilterObj.updateTimeEnd;
+      if (start || end) {
+        if (start && !end) {
+          advanceFilterArr.push({
+            key: "updateTimeStart",
+            text: `最后更新时间:>= ${this.formatTimeToStr(
+              this.advanceFilterObj.updateTimeStart
+            )}`
+          });
+        } else if (!start && end) {
+          advanceFilterArr.push({
+            key: "updateTimeEnd",
+            text: `最后更新时间:<= ${this.formatTimeToStr(
+              this.advanceFilterObj.updateTimeEnd
+            )}`
+          });
+        } else if (start && end) {
+          advanceFilterArr.push({
+            key: "updateTime",
+            text: `最后更新时间:${this.formatTimeToStr(
+              this.advanceFilterObj.updateTimeStart
+            )} ~ ${this.formatTimeToStr(this.advanceFilterObj.updateTimeEnd)}`
+          });
+        }
+      }
+      this.advanceFilterArr = advanceFilterArr;
+    },
+    // 确定点击
+    handleSave() {
+      const isOk = this.getIsOk();
+      if (!isOk) {
+        this.isOk = false;
+        return false;
+      }
+      this.$parent.setAdvanceFilterObj(this.advanceFilterObj);
+      this.$parent.setAdvanceFilterArr(this.advanceFilterArr);
+      this.$parent.handleShowAdvanceFilter(false);
+    },
+    handleCancel() {
+      this.$parent.handleShowAdvanceFilter(false);
+    },
+    // 清空点击
+    handleClear() {
+      this.advanceFilterObj = { ...ADVANCE_FILTERS };
+    },
+    // 加载用户列表
+    loadUser() {
+      const params = {};
+      params.url = this.$apis.api_material_user_sels;
+      params.data = {
+        userName: ""
+      };
+      this.$net.req(params).then(
+        res => {
+          const { data } = res;
+          if (data) {
+            this.optionsObj.userList = data;
+            this.$nextTick(() => {
+              this.advanceFilterObj = {
+                ...this.advanceFilterObj,
+                ...this.advanceFilters
+              };
+            });
+          }
+        },
+        () => {}
+      );
+    }
+  }
+};
+</script>
+
+<style lang="less">
+.filter-dialog {
+  .el-input--prefix .el-input__inner {
+    padding: 0 22px;
+  }
+  .el-input__prefix {
+    left: 0;
+  }
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  height: 100vh;
+  .el-dialog {
+    position: relative !important;
+    top: 0 !important;
+    left: 0 !important;
+    margin: 0 !important;
+    transform: none !important;
+  }
+  .dialog-title {
+    display: flex;
+    flex-direction: column;
+    // height: 106px;
+    .top {
+      display: flex;
+      justify-content: space-between;
+      h2 {
+        font-size: 17px;
+        font-family: PingFangSC-Regular, PingFangSC;
+        font-weight: 400;
+        color: #1a1a1a;
+        padding: 20px 26px;
+      }
+      .el-icon-close {
+        color: #999;
+        margin: 12px 12px 0 0;
+        width: 30px;
+        height: 30px;
+        font-size: 15px;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        cursor: pointer;
+      }
+    }
+  }
+  .el-dialog__body {
+    padding: 0 26px;
+    width: 100%;
+  }
+  .el-dialog__footer {
+    padding: 0;
+  }
+  .dialog-footer {
+    display: flex;
+    align-items: center;
+    overflow: hidden;
+    justify-content: space-between;
+    background-color: #ffffff;
+    padding: 26px;
+    .tips {
+      color: #ff523d;
+      font-size: 13px;
+    }
+    .footer-wrap {
+      display: flex;
+      align-items: center;
+    }
+    .dialog-pagination {
+      display: flex;
+      align-items: center;
+      .el-pagination {
+        padding: 0 5px;
+        height: 32px;
+        > *,
+        .el-input__inner,
+        .el-pager li {
+          height: 32px;
+          line-height: 32px;
+        }
+      }
+    }
+  }
+}
+</style>
+
+<style lang="less" scoped>
+.filter-wrap {
+  overflow: hidden;
+  // display: flex;
+  // flex-direction: column;
+  .filter-item {
+    float: left;
+    margin-bottom: 20px;
+    width: 100%;
+    .filter-title {
+      font-size: 13px;
+      color: #666666;
+      margin-bottom: 8px;
+    }
+    .filter-info {
+      display: flex;
+      .line {
+        font-size: 20px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        color: #666666;
+        padding: 0 5px;
+      }
+    }
+  }
+}
+</style>

+ 58 - 0
src/components/daily4StudyAbroad/author/PagePagination.vue

@@ -0,0 +1,58 @@
+<template>
+  <section class="page-ft">
+    <el-pagination
+      :current-page="filterObj.page"
+      :page-sizes="[10, 20, 30, 50, 100]"
+      :page-size="filterObj.limit"
+      :total="totalCount"
+      background
+      layout="total, sizes, prev, pager, next, jumper"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+    ></el-pagination>
+  </section>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      totalCount: 0, // table总条数
+      filterObj: {
+        page: 1, // 当前页数
+        limit: 20 // 每页显示数量
+      }
+    };
+  },
+  methods: {
+    // 设置totalCount总数
+    setTotalCount(totalCount) {
+      this.totalCount = totalCount;
+    },
+    // 设置页数
+    setPage(page) {
+      this.filterObj.page = page;
+    },
+    // 往父组件暴露筛选条件对象filterObj
+    getFilterObj() {
+      return JSON.parse(JSON.stringify(this.filterObj));
+    },
+    // 分页器监听页数变化
+    handleSizeChange(limit) {
+      this.filterObj.limit = limit;
+      const pageTableList = this.$parent.$refs.pageTableList;
+      if (pageTableList) {
+        pageTableList.loadData();
+      }
+    },
+    // 分页器监听页号变化
+    handleCurrentChange(page) {
+      this.filterObj.page = page;
+      const pageTableList = this.$parent.$refs.pageTableList;
+      if (pageTableList) {
+        pageTableList.loadData();
+      }
+    }
+  }
+};
+</script>

+ 159 - 0
src/components/daily4StudyAbroad/author/PageTableList.vue

@@ -0,0 +1,159 @@
+<template>
+  <section class="page-bd">
+    <el-table
+      class="table-list"
+      :data="tableData"
+      :border="true"
+      :fit="true"
+      :highlight-current-row="true"
+      current-row-key="name"
+      row-class-name="hooli-table-tr"
+      cell-class-name="hooli-table-td"
+      header-row-class-name="hooli-table-hd-tr"
+      header-cell-class-name="hooli-table-hd-td"
+      v-loading="isLoading"
+      element-loading-custom-class="hooli-loading"
+      row-key="id"
+      @row-click="handleRowClick"
+    >
+      <template v-for="(column, index) in columnArr">
+        <template v-if="column.isVisible">
+          <el-table-column
+            v-if="column.prop === 'statusText'"
+            :type="column.type"
+            :prop="column.prop"
+            :label="column.label"
+            :width="column.width"
+            :min-width="column['min-width']"
+            :sortable="column.sortable"
+            :align="column.align"
+            :header-align="column['header-align']"
+            :key="index"
+            :show-overflow-tooltip="column['show-overflow-tooltip']"
+          >
+            <div class="status-wrap" slot-scope="scope">
+              <span
+                :class="[
+                  'dot',
+                  scope.row.statusText && scope.row.statusText.includes('上')
+                    ? 'up'
+                    : 'down'
+                ]"
+              ></span>
+              <span class="text">{{ scope.row.statusText }}</span>
+            </div>
+          </el-table-column>
+          <el-table-column
+            v-else
+            :type="column.type"
+            :prop="column.prop"
+            :label="column.label"
+            :width="column.width"
+            :min-width="column['min-width']"
+            :sortable="column.sortable"
+            :align="column.align"
+            :header-align="column['header-align']"
+            :key="index"
+            :show-overflow-tooltip="column['show-overflow-tooltip']"
+          ></el-table-column>
+        </template>
+      </template>
+    </el-table>
+  </section>
+</template>
+
+<script>
+export default {
+  props: {
+    // 列数组,按用户设置的顺序,包含所有需显示和不需显示的列,渲染时需根据isVisible过滤
+    columnArr: {
+      type: Array,
+      required: true,
+      default: () => []
+    }
+  },
+  data() {
+    return {
+      isLoading: false, // table loading
+      tableData: [] // table data
+    };
+  },
+  methods: {
+    // 暴露给外组件用的tableData
+    getTableData() {
+      return JSON.parse(JSON.stringify(this.tableData));
+    },
+    // 表格行点击,加载 详情 组件
+    handleRowClick(row, column, event) {
+      event.stopPropagation();
+      this.$parent.loadDrawer("detail", row.id);
+    },
+    // 加载表格数据,isSlient=true静默加载,不显示loading
+    loadData(isSlient = false) {
+      const params = {
+        url: this.$apis.api_operation_material_author_list
+      };
+      const filterObj = {}; // 筛选条件
+      const pageHeader = this.$parent.$refs.pageHeader;
+      const pagePagination = this.$parent.$refs.pagePagination;
+      if (pageHeader) {
+        Object.assign(filterObj, pageHeader.getFilterObj()); // 条件筛选在PageHeader兄弟组件返回
+      }
+      if (pagePagination) {
+        Object.assign(filterObj, pagePagination.getFilterObj()); // 分页筛选条件在PagePagination兄弟组件返回
+      }
+      params.data = this.$utils.removeEmptyProperty(filterObj);
+      this.$net.req(params).then(
+        res => {
+          !isSlient && (this.isLoading = false);
+          const { data } = res;
+          if (data) {
+            const { list, pages } = data;
+            if (Array.isArray(list)) {
+              this.tableData = list;
+              if (pagePagination) {
+                pagePagination.setTotalCount(pages.totalCount);
+              }
+              return false;
+            }
+          }
+          this.tableData = [];
+          this.$message.error(
+            (res && res.msg) || "Oops, something seems went wrong~"
+          );
+        },
+        e => {
+          !isSlient && (this.isLoading = false);
+          this.tableData = [];
+          this.$message.error(
+            (e && e.msg) || "Oops, something seems went wrong~"
+          );
+        }
+      );
+      !isSlient && (this.isLoading = true);
+    }
+  }
+};
+</script>
+
+<style lang="less" scoped>
+.status-wrap {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  .dot {
+    width: 8px;
+    height: 8px;
+    border-radius: 8px;
+    &.up {
+      background-color: #00d388;
+    }
+    &.down {
+      background-color: #e1e1e1;
+    }
+  }
+  .text {
+    margin-left: 4px;
+  }
+}
+</style>

+ 19 - 16
src/components/daily4StudyAbroad/video/CreateEdit.vue

@@ -153,6 +153,7 @@
                   filterable
                   allow-create
                   clearable
+                  multiple
                   default-first-option
                   placeholder="请选择关联话题"
                   no-data-text="未搜索到关联话题"
@@ -235,7 +236,7 @@ export default {
         title: "", // 标题
         authorId: "", // 作者ID
         categoryId: "", // 类目ID
-        tagList: "", // 话题,[{id:'',tagName: ''}],目前一个,之后会多个
+        tagList: [], // 话题,[{id:'',tagName: ''}],目前一个,之后会多个
         status: 2 // 上架状态,默认下架
       },
       // 表单校验规则
@@ -306,17 +307,21 @@ export default {
           };
         }
         const formData = { ...this.formData };
-        this.tagArr.forEach(item => {
-          if (item.id === formData.tagList) {
-            formData.tagList = {
-              id: item.id,
-              tagName: item.tagName
-            };
-          }
-        });
-        formData.tagList = JSON.stringify(
-          formData.tagList ? [formData.tagList] : []
-        );
+
+        // this.tagArr.forEach(item => {
+        //   if (item.id === formData.tagList) {
+        //     formData.tagList = formData.tagList.concat({
+        //       id: item.id,
+        //       tagName: item.tagName
+        //     });
+        //   }
+        // });
+        const tagList = this.tagArr
+          .filter(item => formData.tagList.indexOf(item.id) != -1)
+          .map(item => {
+            return { id: item.id, tagName: item.tagName };
+          });
+        formData.tagList = JSON.stringify(tagList);
         Object.assign(params.data, formData);
         params.data = this.$utils.removeEmptyProperty(params.data);
         this.$net.req(params).then(
@@ -555,12 +560,10 @@ export default {
         title: obj.title || "",
         authorId: obj.authorId || "",
         categoryId: obj.categoryId || "",
-        tagList: "",
+        tagList: [],
         status: obj.status || 2
       };
-      if (Array.isArray(obj.tagList) && obj.tagList.length > 0) {
-        formData.tagList = obj.tagList[0].id;
-      }
+      formData.tagList = obj.tagList.map(item => item.id);
       this.formData = Object.assign(this.formData, formData);
       this.videoPosterDefault = this.formData.videoCoverImgUrl;
     }

+ 9 - 0
src/pages/home/run.vue

@@ -278,6 +278,15 @@ const MENU_ARR = [
         route: "daily-4-study-abroad-category",
         permissionKey: "",
         child: []
+      },
+      {
+        id: "10-6",
+        icon: "",
+        text: "作者",
+        unReadNum: 0,
+        route: "daily-4-study-abroad-author",
+        permissionKey: "",
+        child: []
       }
     ]
   }

+ 8 - 0
src/router.js

@@ -166,6 +166,14 @@ const routes = [
                 "@/components/daily4StudyAbroad/category/Page.vue"
               ], resolve)
           },
+          {
+            path: "daily-4-study-abroad-author",
+            name: "daily-4-study-abroad-author",
+            component: resolve =>
+              require([
+                "@/components/daily4StudyAbroad/author/Page.vue"
+              ], resolve)
+          },
           {
             path: "",
             redirect: "index"