vbea 2 лет назад
Родитель
Сommit
58dff65164

+ 2 - 2
Strides-Admin/src/api/campaign.js

@@ -23,8 +23,8 @@ const campaign = {
   deleteCampaignLink(linkId) {
     return del("campaign/campaigns-links/" + linkId)
   },
-  deleteCampaignDiscount(linkId) {
-    return del("campaign/campaigns-discounts/" + linkId)
+  deleteCampaignDiscount(discountId) {
+    return del("campaign/campaigns-discounts/" + discountId)
   },
   getAssignStatusOptions() {
     return get("campaign/assignment-statuses")

+ 10 - 2
Strides-Admin/src/router/MarketingRouter.js

@@ -70,7 +70,11 @@ export default {
       name: 'campaign-management-add',
       meta: {
         title: 'Create',
-        activeMenu: '/marketing-management/campaign'
+        activeMenu: '/marketing-management/campaign',
+        parent: {
+          title: 'Campaigns',
+          path: "/marketing-management/campaign"
+        }
       },
       hidden: true
     },
@@ -80,7 +84,11 @@ export default {
       name: 'campaign-management-update',
       meta: {
         title: 'Update',
-        activeMenu: '/marketing-management/campaign'
+        activeMenu: '/marketing-management/campaign',
+        parent: {
+          title: 'Campaigns',
+          path: "/marketing-management/campaign"
+        }
       },
       hidden: true
     },

+ 2 - 3
Strides-Admin/src/views/article/detail.vue

@@ -63,7 +63,7 @@
             label-width="0">
             <label
               class="el-form-item__label"
-              style="float: none;">Photos (Max 4 photos and 2mb each)</label>
+              style="float: none;">Photos (Max 4 photos and 2mb each):</label>
             <div class="flexcr">
               <el-upload
                 class="logo-upload"
@@ -179,8 +179,7 @@ export default {
         articleTitle: "",
         articleContent: "",
         articleImagePaths: [],
-        articleLinks: [],
-        createTime: ""
+        articleLinks: []
       },
       options: {
         type: []

+ 311 - 0
Strides-Admin/src/views/campaign/assignment.vue

@@ -0,0 +1,311 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible="visible"
+    :before-close="onHide"
+    class="assign-campaign-dialog">
+    <div class="filter-container filter-view">
+      <el-select
+        style="min-width: 70px; max-width: 120px;"
+        clearable
+        v-model="filter.pageVo.assignmentStatus"
+        placeholder="Status"
+        @change="onSearch">
+        <el-option
+          v-for="(item, index) in statusOptions"
+          :key="index"
+          :label="item"
+          :value="item"/>
+      </el-select>
+      <div style="flex: 1; min-width: 150px; max-width: 300px;">
+        <el-input
+          clearable
+          v-model="filter.pageVo.criteria"
+          placeholder="Search by Site Name or Service Provider"
+          @keyup.enter.native="onSearch"/>
+      </div>
+      <el-button
+        type="primary"
+        @click="onSearch">
+        Search
+      </el-button>
+    </div>
+    <div class="assign-label-actions">
+      <el-button
+        type="danger"
+        :disabled="selectRow.length == 0"
+        :loading="loading.unassign"
+        @click="onClickUnassign">
+        Batch Un-assign
+      </el-button>
+      <el-button
+        type="accent"
+        :disabled="selectRow.length == 0"
+        :loading="loading.assign"
+        @click="onClickAssign">
+        Batch Assign
+      </el-button>
+    </div>
+    <div class="table-view" v-loading="table.loading">
+      <el-table
+        :data="table.data"
+        height="100%"
+        class="no-border"
+        @selection-change="changeSelection">
+        <el-table-column
+          align="center"
+          label="Site Name"
+          prop="siteName"
+          min-width="120"/>
+        <el-table-column
+          align="center"
+          label="Address"
+          prop="address"
+          min-width="120"/>
+        <el-table-column
+          align="center"
+          label="Service Provider"
+          min-width="140">
+          <template slot-scope="{row}">
+            <div v-for="item in row.serviceProviders" :key="item">{{item}}</div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          align="center"
+          label="Assignment Status"
+          prop="assignmentStatus"
+          min-width="120"/>
+        <el-table-column
+          align="center"
+          label="Select"
+          type="selection"/>
+      </el-table>
+    </div>
+    <div class="center" style="margin-bottom: -20px;">
+      <Pagination
+        v-show="table.total"
+        :total="table.total"
+        :page.sync="filter.pageNo"
+        :limit.sync="filter.pageSize"
+        @pagination="getTableData"/>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import api from '../../api/campaign.js'
+import Pagination from '@/components/Pagination'
+export default {
+  name: "AssignmentSiteLabel",
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    item: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      filter: {
+        pageSize: 10,
+        pageNo: 1,
+        pageVo: {
+          criteria: "",
+          campaignId: "",
+          assignmentStatus: ""
+        }
+      },
+      table: {
+        data: [],
+        total: 0,
+        loading: false
+      },
+      loading: {
+        assign: false,
+        unassign: false
+      },
+      statusOptions: [],
+      selectRow: []
+    };
+  },
+  components: {Pagination},
+  computed: {
+    title() {
+      return "ASSIGN SITES (CAMPAIGN: " + this.item.campaignTitle + ")" 
+    }
+  },
+  watch: {
+    visible: {
+      handler(n, o) {
+        console.log("watch.visible", n, o);
+        if (n) {
+          this.filter.pageVo.campaignId = this.item.campaignId;
+          this.filter.pageVo.assignmentStatus = "";
+          this.onSearch()
+        }
+      }
+    }
+  },
+  mounted() {
+    this.getStatusOptions();
+  },
+  methods: {
+    onHide() {
+      this.$emit("hide", true);
+    },
+    onSearch() {
+      this.filter.pageNo = 1;
+      this.getTableData();
+    },
+    getStatusOptions() {
+      api.getAssignStatusOptions().then(res => {
+        if (res.data) {
+          this.statusOptions = res.data
+        }
+      }).catch(error => {
+        this.$message({
+          type: 'error',
+          message: error
+        })
+      })
+    },
+    getTableData() {
+      api.getCampaignAssignPages(this.filter).then(res => {
+        if (res.total && res.data) {
+          this.table.total = res.total;
+          this.table.data = res.data;
+        } else {
+          this.table.total = 0;
+          this.table.data = [];
+        }
+        this.table.loading = false;
+      }).catch(error => {
+        this.$message({
+          type: 'error',
+          message: error
+        })
+        this.table.total = 0;
+        this.table.data = [];
+        this.table.loading = false;
+      })
+    },
+    changeSelection(val) {
+      this.selectRow = val;
+    },
+    getSelectIds() {
+      const ids = [];
+      this.selectRow.forEach(item => {
+        ids.push(item.sitePk)
+      })
+      return ids;
+    },
+    onClickAssign() {
+      const params = {
+        campaignId: this.item.campaignId,
+        sitePks: this.getSelectIds()
+      }
+      this.loading.assign = true;
+      api.assignSites(params).then(res => {
+        this.$message({
+          type: 'success',
+          message: res.msg || "Success"
+        })
+        this.getTableData()
+      }).catch(error => {
+        this.$message({
+          type: 'error',
+          message: error
+        })
+      }).finally(() => {
+        this.loading.assign = false;
+      })
+    },
+    onClickUnassign() {
+      const params = {
+        campaignId: this.item.campaignId,
+        sitePks: this.getSelectIds()
+      }
+      this.loading.unassign = true;
+      api.unassignSites(params).then(res => {
+        this.$message({
+          type: 'success',
+          message: res.msg || "Success"
+        })
+        this.getTableData()
+      }).catch(error => {
+        this.$message({
+          type: 'error',
+          message: error
+        })
+      }).finally(() => {
+        this.loading.unassign = false;
+      })
+    },
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.assign-campaign-dialog
+  ::v-deep .el-dialog {
+    width: 65vw;
+    height: 90vh;
+    display: flex;
+    max-width: 1200px;
+    flex-direction: column;
+    margin-top: 5vh !important;
+  .el-dialog__header {
+    padding: 20px 20px 0;
+    font-weight: bold;
+  }
+  .el-dialog__body {
+    flex: 1;
+    padding: 20px;
+    display: flex;
+    overflow: hidden;
+    flex-direction: column;
+  }
+  @media screen and (max-width: 1200px) {
+    & {
+      width: 70vw;
+    }
+  }
+  @media screen and (max-width: 1000px) {
+    & {
+      width: 80vw;
+    }
+  }
+  @media screen and (max-width: 800px) {
+    & {
+      width: 90vw;
+    }
+  }
+  @media screen and (max-width: 700px) {
+    & {
+      width: 99vw;
+    }
+  }
+  @media screen and (max-width: 320px) {
+    & {
+      width: 100%;
+      min-width: 300px;
+    }
+  }
+}
+.assign-campaign-dialog .table-view {
+  flex: 1;
+  overflow-y: auto;
+  padding-top: 10px;
+  margin-bottom: -10px;
+}
+.assign-label-actions {
+  display: flex;
+  padding-top: 5px;
+  flex-wrap: wrap-reverse;
+  align-items: center;
+  justify-content: flex-end;
+}
+</style>

+ 729 - 7
Strides-Admin/src/views/campaign/detail.vue

@@ -1,25 +1,747 @@
 <template>
-  <div>
-    
+  <div class="container" v-loading="loading">
+    <el-form
+      :model="form"
+      :rules="rules"
+      ref="form"
+      label-position="right"
+      label-width="130px">
+      <div class="flexr">
+        <div class="content flex1">
+          <div class="section-title">Campaign Detail</div>
+          <el-form-item
+            prop="campaignTitle"
+            label="Campaign Title:">
+            <el-input
+              v-model="form.campaignTitle"
+              class="add-text"
+              placeholder=""
+              maxlength="100"/>
+          </el-form-item>
+          <el-form-item
+            prop="startTime"
+            label="Start Date Time:">
+            <el-date-picker
+              v-model="form.startTime"
+              class="add-text"
+              type="datetime"
+              format="yyyy-MM-dd HH:mm"
+              value-format="yyyy-MM-dd HH:mm"/>
+          </el-form-item>
+          <el-form-item
+            prop="endTime"
+            label="End Date Time:">
+            <el-date-picker
+              v-model="form.endTime"
+              class="add-text"
+              type="datetime"
+              format="yyyy-MM-dd HH:mm"
+              value-format="yyyy-MM-dd HH:mm"
+              :default-value="form.startTime"/>
+          </el-form-item>
+          <el-form-item
+            prop="campaignContent"
+            label=""
+            label-width="0">
+            <label
+              class="el-form-item__label"
+              style="width: 150px;">Campaign Content:</label>
+            <el-input
+              v-model="form.campaignContent"
+              class="area-text"
+              type="textarea"
+              placeholder=""
+              maxlength="5000"
+              :autosize="autoSize"/>
+            <div class="add-checkbox" v-if="!isEdit">
+              <el-checkbox
+                v-model="form.switch"
+                label="Use Campaign Title and Text to Create Notification Post"/>
+            </div>
+          </el-form-item>
+        </div>
+        <div class="content flex1">
+          <div class="section-title">Images & Links</div>
+          <el-form-item
+            label=""
+            label-width="0">
+            <label
+              class="el-form-item__label"
+              style="float: none;">Photos (Max 4 photos and 2mb each):</label>
+            <div class="flexcr">
+              <el-upload
+                class="logo-upload"
+                action
+                :limit="1"
+                :show-file-list="false"
+                :file-list="[]"
+                :http-request="file => uploadImages(file, index)"
+                accept=".jpg,.jpeg,.png,.gif,.JPG,.JPEG"
+                v-loading="item.loading"
+                v-for="(item, index) in images">
+                <div class="uploader-image" v-if="item.articleImagePath">
+                  <el-image
+                    :src="$imageSrc(item.articleImagePath)"
+                    title="Click to update image"/>
+                  <i
+                    title="Click to delete image"
+                    class="uploader-del el-icon-delete"
+                    @click.stop="deleteImages(index)"></i>
+                </div>
+                <i v-else
+                  class="el-icon-plus avatar-uploader-icon"
+                  title="Click to select file"/>
+              </el-upload>
+            </div>
+          </el-form-item>
+          <el-form-item
+            label=""
+            label-width="0">
+            <label
+              class="el-form-item__label"
+              style="float: none;">LINKS:</label>
+            <div style="max-width: 600px; display: grid;">
+              <el-table
+                class="article-links-table no-border"
+                :data="form.links">
+                <el-table-column
+                  label="Name Of Link"
+                  min-width="120px">
+                  <template slot-scope="{row}">
+                    <el-input
+                      v-model="row.articleLinkName"
+                      maxlength="100"/>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label="Hyperlink"
+                  min-width="130px">
+                  <template slot-scope="{row}">
+                    <el-input
+                      v-model="row.articleLink"
+                      maxlength="150"/>
+                  </template>
+                </el-table-column>
+                <el-table-column
+                  label=""
+                  width="90px">
+                  <template slot-scope="{row,$index}">
+                    <div class="flexc" v-loading="row.loading">
+                      <i
+                        class="list-item-icon el-icon-remove-outline"
+                        style="color: #ED3F3F;"
+                        @click="onRemoveLink($index)"/>
+                      <i
+                        v-if="$index === form.links.length - 1"
+                        class="list-item-icon el-icon-circle-plus-outline"
+                        style="color: #82CF08;"
+                        @click="addLinkObj"/>
+                    </div>
+                  </template>
+                </el-table-column>
+              </el-table>
+            </div>
+          </el-form-item>
+        </div>
+      </div>
+      <div class="content">
+        <div class="section-title">Configure Discount</div>
+        <el-table
+          class="article-links-table no-border"
+          :data="form.discounts"
+          style="max-width: 800px;">
+          <el-table-column
+            label="User Type"
+            min-width="80px">
+            <template slot-scope="{row,$index}">
+              <el-select
+                style="width: 100%;"
+                :value="row.userType"
+                @change="type => changeUserType($index, type)"
+                clearable>
+                <el-option
+                  v-for="item in userTypeOptions"
+                  :key="item"
+                  :label="item"
+                  :value="item" />
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="Group Name"
+            min-width="120px">
+            <template slot-scope="{row}">
+              <el-select
+                style="width: 100%;"
+                v-if="row.groupList.length"
+                :disabled="row.userType == 'PUBLIC'"
+                v-model="row.groupPk"
+                clearable>
+                <el-option
+                  v-for="(item,index) in row.groupList"
+                  :key="index"
+                  :label="item.name"
+                  :value="item.value"/>
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="Discount (%)"
+            min-width="80px">
+            <template slot-scope="{row}">
+              <el-input
+                v-model="row.discount"
+                maxlength="5"/>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label=""
+            width="90px">
+            <template slot-scope="{row,$index}">
+              <div class="flexc" v-loading="row.loading">
+                <i
+                  class="list-item-icon el-icon-remove-outline"
+                  style="color: #ED3F3F;"
+                  @click="onRemoveDiscount($index)"/>
+                <i
+                  v-if="$index === form.discounts.length - 1"
+                  class="list-item-icon el-icon-circle-plus-outline"
+                  style="color: #82CF08;"
+                  @click="addDiscountObj"/>
+              </div>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <div class="content flexcr">
+        <div class="buttons">
+          <el-button
+            @click="onClickCancel"
+            type="primary"
+            class="cancel-button">
+            Cancel
+          </el-button>
+          <el-button
+            @click="onClickSave"
+            type="primary">
+            Save
+          </el-button>
+        </div>
+        <div
+          class="update-by"
+          v-if="isEdit">
+          <span
+            class="add-text"
+            :title='"CREATED BY " + form.createdBy + " ON " + form.createdOn'>
+            LAST UPDATED BY {{form.updatedBy}} TIMESTAMP: {{form.updatedOn}}
+          </span>
+        </div>
+      </div>
+    </el-form>
   </div>
 </template>
 
 <script>
+import api from '../../api/campaign.js'
+import apiUser from '@/http/api/apiUser'
 export default {
   data() {
     return {
-      
+      loading: false,
+      form: {
+        campaignId: "",
+        startTime: "",
+        endTime: "",
+        campaignTitle: "",
+        campaignContent: "",
+        sendNotification: true,
+        imagePaths: [],
+        links: [],
+        discounts: []
+      },
+      options: {
+        type: []
+      },
+      images: [{
+        articleImagePath: "",
+        loading: false
+      }],
+      isEdit: false,
+      rules: {
+        startTime: [{
+          message: "Please select start date time",
+          trigger: "change",
+          required: true,
+        }],
+        endTime: [{
+          message: "Please select end date time",
+          trigger: "change",
+          required: true,
+        }],
+        campaignTitle: [{
+          message: "Please input campaign title",
+          trigger: "blur",
+          required: true,
+        }],
+        campaignContent: [{
+          message: "Please input campaign content",
+          trigger: "blur",
+          required: true,
+        }]
+      },
+      autoSize: {
+        minRows: 20,
+        maxRows: 50
+      },
+      userTypeOptions: [],
+      groupOptions: {
+        PUBLIC: [{
+          name: "None",
+          value: ""
+        }]
+      }
     };
   },
   created() {
-    
+    this.addLinkObj();
+    this.addDiscountObj();
+    this.getUserTypeOption();
+    if (this.$route.params.id) {
+      this.loading = true;
+      this.isEdit = true;
+      this.getCampaignDetail();
+    }
   },
   methods: {
-    
+    onClickCancel() {
+      this.$nextTick(() => {
+        this.$router.replace({
+          path: "/marketing-management/campaign"
+        })
+      })
+    },
+    getUserTypeOption() {
+      apiUser.getUserTypeOptions().then(res => {
+        if (res.data) {
+          this.userTypeOptions = res.data
+        }
+      }).catch(err => {
+        this.$message({
+          message: error,
+          type: 'error'
+        })
+      })
+    },
+    getCampaignDetail() {
+      api.viewCampaign(this.$route.params.id).then(res => {
+        if (res.data) {
+          let info = res.data
+          this.images = []
+          info.imagePaths.forEach(item => {
+            this.images.push({
+              ...item,
+              loading: false
+            })
+          })
+          info.discounts.forEach(item => {
+            item.groupList = []
+          })
+          this.form = info;
+          this.$nextTick(() => {
+            this.form.discounts.forEach((item, index) => {
+              this.changeUserType(index, item.userType)
+            })
+          })
+          this.addImageObj();
+        }
+      }).catch(err => {
+        this.$message.error(err)
+      }).finally(() => {
+        this.loading = false;
+      })
+    },
+    addImageObj() {
+      if (this.images.length < 4) {
+        this.images.push({
+          articleImagePath: "",
+          loading: false
+        })
+      }
+    },
+    uploadImages(file, index) {
+      this.images[index].loading = true;
+      const formData = new FormData()
+      formData.append('file', file.file)
+      api.uploadImages(formData).then(res => {
+        if (res.data.picturePath) {
+          if (!this.images[index].articleImagePath) {
+            this.addImageObj();
+          }
+          this.images[index].articleImagePath = res.data.picturePath
+        }
+      }).catch(err => {
+        this.$message({
+          message: err,
+          type: 'error'
+        })
+      }).finally(() => {
+        this.images[index].loading = false;
+      })
+    },
+    deleteImages(index) {
+      const item = this.images[index];
+      if (item.articleImageId) {
+        this.$confirm('Confirm delete?', 'Delete', {
+          confirmButtonText: 'Confirm',
+          cancelButtonText: 'Cancel',
+          type: 'warning'
+        }).then(res => {
+          this.deleteImageById(item.articleImageId, index);
+        })
+      } else {
+        this.images.splice(index, 1);
+        this.addImageObj()
+      }
+    },
+    deleteImageById(id, index) {
+      this.images[index].loading = true;
+      api.deleteCampaignImage(id).then(res => {
+        this.images.splice(index, 1);
+        this.addImageObj()
+      }).catch(err => {
+        this.$message({
+          message: err,
+          type: 'error'
+        })
+        this.images[index].loading = false;
+      })
+    },
+    addLinkObj() {
+      this.form.links.push({
+        loading: false,
+        articleLink: "",
+        articleLinkName: ""
+      })
+    },
+    onRemoveLink(index) {
+      const item = this.form.links[index];
+      if (item.articleLinkId) {
+        this.$confirm('Confirm delete?', 'Delete', {
+          confirmButtonText: 'Confirm',
+          cancelButtonText: 'Cancel',
+          type: 'warning'
+        }).then(res => {
+          this.deleteLinkById(item.articleLinkId, index);
+        })
+      } else {
+        this.form.links.splice(index, 1);
+        if (this.form.links.length == 0) {
+          this.addLinkObj()
+        }
+      }
+    },
+    deleteLinkById(id, index) {
+      this.form.links[index].loading = true;
+      api.deleteCampaignLink(id).then(res => {
+        this.form.links.splice(index, 1);
+        if (this.form.links.length == 0) {
+          this.addLinkObj()
+        }
+      }).catch(err => {
+        this.$message({
+          message: err,
+          type: 'error'
+        })
+        this.form.links[index].loading = false;
+      })
+    },
+    addDiscountObj() {
+      this.form.discounts.push({
+        loading: false,
+        userType: "",
+        groupPk: "",
+        discount: "",
+        groupList: []
+      })
+    },
+    onRemoveDiscount(index) {
+      const item = this.form.discounts[index];
+      if (item.articleDiscountId) {
+        this.$confirm('Confirm delete?', 'Delete', {
+          confirmButtonText: 'Confirm',
+          cancelButtonText: 'Cancel',
+          type: 'warning'
+        }).then(res => {
+          this.deleteDiscountById(item.articleDiscountId, index);
+        })
+      } else {
+        this.form.discounts.splice(index, 1);
+        if (this.form.discounts.length == 0) {
+          this.addDiscountObj()
+        }
+      }
+    },
+    deleteDiscountById(id, index) {
+      this.form.discounts[index].loading = true;
+      api.deleteCampaignDiscount(id).then(res => {
+        this.form.discounts.splice(index, 1);
+        if (this.form.discounts.length == 0) {
+          this.addDiscountObj()
+        }
+      }).catch(err => {
+        this.$message({
+          message: err,
+          type: 'error'
+        })
+        this.form.discounts[index].loading = false;
+      })
+    },
+    changeUserType(dsIndex, dsType) {
+      let type = this.form.discounts[dsIndex].userType;
+      if (dsType != type) {
+        this.form.discounts[dsIndex].userType = dsType;
+        this.form.discounts[dsIndex].groupPk = "";
+      }
+      if (dsType) {
+        if (this.groupOptions[dsType]) {
+          this.form.discounts[dsIndex].groupList = this.groupOptions[dsType];
+        } else {
+          this.groupOptions[dsType] = []
+          this.getGroupOptions(dsIndex, dsType);
+        }
+      } else {
+        this.form.discounts[dsIndex].groupList = []
+      }
+    },
+    getGroupOptions(index, type) {
+      apiUser.getGroupByType(type).then(res => {
+        if (res.data) {
+          this.groupOptions[type] = res.data
+          this.changeUserType(index, type)
+        }
+      }).catch(err => {
+        this.$message({
+          message: err,
+          type: 'error'
+        })
+        this.groupOptions[type] = ""
+      })
+    },
+    onClickSave() {
+      this.$refs['form'].validate((valid) => {
+        if (valid) {
+          const images = this.images.filter(item => item.articleImagePath)
+          if (images.length == 0) {
+            this.$message({
+              message: "Please upload at least one image",
+              type: 'error'
+            })
+            return;
+          }
+          const links = this.form.links.filter(item => item.articleLinkName);
+          if (links.length == 0) {
+            this.$message({
+              message: "Please add at least one link",
+              type: 'error'
+            })
+            return;
+          }
+          const discounts = this.form.discounts.filter(item => {
+            if (item.userType == "PUBLIC") {
+              return item.userType && item.discount
+            } else {
+              return item.userType && item.groupPk && item.discount
+            }
+          })
+          const params = {
+            ...this.form,
+            links: links,
+            imagePaths: images,
+            discounts: discounts
+          }
+          this.loading = true;
+          this.isEdit ? this.updateArticle(params) : this.addArticle(params);
+        }
+      })
+    },
+    addArticle(params) {
+      api.addCampaign(params).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Add successfully"
+        });
+        this.onClickCancel();
+      }).catch(err => {
+        this.loading = false;
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      });
+    },
+    updateArticle(params) {
+      api.updateCampaign(params).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Update successfully"
+        });
+        this.onClickCancel();
+      }).catch(err => {
+        this.loading = false;
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      });
+    }
   }
 }
 </script>
 
-<style scoped>
-
+<style lang="scss" scoped>
+  @import '../../styles/variables.scss';
+  .container {
+    width: 100%;
+    padding: 20px 60px;
+    min-height: $mainAppMinHeight;
+    background-color: #F0F5FC;
+  }
+  .content {
+    min-width: 400px;
+    margin: 0 8px 16px;
+    padding: 15px 60px;
+    border-radius: 6px;
+    background-color: white;
+  }
+  
+  .section-title {
+    color: #333;
+    margin-top: 20px;
+    margin-bottom: 30px;
+    font-size: 15px;
+    user-select: none;
+    line-height: 24px;
+    font-weight: bold;
+    font-family: sans-serif;
+    text-transform: uppercase;
+  }
+  .add-text {
+    width: 100%;
+    min-width: 200px;
+  }
+  
+  .area-text {
+    width: 100%;
+    min-width: 200px;
+  }
+  
+  .add-text ::v-deep .el-textarea__inner,
+  .area-text ::v-deep .el-textarea__inner {
+    font-family: sans-serif;
+  }
+  .add-checkbox {
+    overflow: auto;
+    padding: 10px 0;
+    &::v-deep {
+      .el-checkbox.is-checked .el-checkbox__label{
+        font-weight: bold;
+      }
+    }
+  }
+  
+  
+  
+  .hr {
+    height: 2px;
+    margin: 10px -40px;
+    background-color: #F0F5FC;
+  }
+  .buttons {
+    padding-top: 15px;
+    padding-bottom: 15px;
+  }
+  @media screen and (max-width: 500px) {
+    .container {
+      padding: 0px;
+    }
+    .content {
+      padding: 15px 30px;
+    }
+  }
+  .logo-upload {
+    width: 148px;
+    height: 148px;
+    position: relative;
+    margin-right: 15px;
+    margin-bottom: 15px;
+    ::v-deep .el-image {
+      width: 148px;
+      height: 148px;
+      border-radius: 6px;
+    }
+  }
+  
+  .avatar-uploader-icon {
+    width: 148px;
+    height: 148px;
+    color: #8c939d;
+    cursor: pointer;
+    font-size: 28px;
+    text-align: center;
+    line-height: 148px;
+    border-radius: 6px;
+    border: 1px dashed #d9d9d9;
+  }
+  
+  .uploader-image {
+    width: 148px;
+    height: 148px;
+    text-align: left;
+    position: relative;
+  }
+  
+  .uploader-image ::v-deep img {
+    object-fit: cover;
+  }
+  
+  .uploader-del {
+    right: 4px;
+    bottom: 4px;
+    color: #fff;
+    padding: 5px;
+    display: none;
+    border-radius: 30px;
+    position: absolute;
+    background-color: rgba(1,1,1,.4);
+  }
+  
+  .uploader-image:hover .uploader-del {
+    display: block;
+  }
+  
+  .article-links-table ::v-deep .el-table__header th {
+    color: #606266;
+    border: none;
+    padding: 10px 0 0;
+    font-weight: normal;
+    background: transparent;
+  }
+  .article-links-table ::v-deep td {
+    border: none;
+    padding: 1px 0;
+    background: #fff !important;
+  }
+  .article-links-table ::v-deep .cell {
+    padding: 0px 8px 10px;
+  }
+  .list-item-icon {
+    height: 30px;
+    cursor: pointer;
+    font-size: 26px;
+    font-weight: 500;
+    line-height: 30px;
+  }
+  .list-item-icon + .list-item-icon {
+    margin-left: 15px;
+  }
 </style>

+ 47 - 7
Strides-Admin/src/views/campaign/index.vue

@@ -38,7 +38,7 @@
           <span
             class="link-type"
             @click="onClickEdit(row)"
-            v-if="!$route.meta.onlyView">
+            v-if="!$route.meta.onlyView && row.dataStatus != 'Inactive'">
             {{ row.campaignId }}
           </span>
           <span v-else>{{ row.campaignId }}</span>
@@ -77,13 +77,29 @@
       <el-table-column
         align="center"
         label="Action"
-        min-width="120"
+        min-width="80"
         v-if="!$route.meta.onlyView">
         <template v-slot="{ row }">
-          <TableAction
-            @edit="onClickEdit(row)"
-            @delete="onClickDelete(row)"
-            v-if="row.dataStatus != 'Inactive'"/>
+          <el-dropdown
+            class="action-dropdown"
+            @command="(v) => handleCommand(v, row)"
+            v-if="row.dataStatus != 'Inactive'">
+            <i class="el-icon-more icon-action"></i>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item
+                command="assignSites">
+                Assign Sites
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="onClickEdit">
+                Edit
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="onClickDelete">
+                Delete
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
         </template>
       </el-table-column>
     </el-table>
@@ -95,12 +111,16 @@
         :limit.sync="filters.pageSize"
         @pagination="getTableData" />
     </div>
+    <DialogAssignment
+      v-bind="dialogAssign"
+      @hide="hideAssignDialog"/>
   </div>
 </template>
 
 <script>
 import TableAction from '@/components/TableAction.vue'
 import Pagination from '@/components/Pagination'
+import DialogAssignment from './assignment.vue'
 import charge from '../../http/api/charge'
 import api from '../../api/campaign.js'
 export default {
@@ -122,10 +142,14 @@ export default {
       options: {
         status: [],
         types: []
+      },
+      dialogAssign: {
+        item: {},
+        visible: false
       }
     };
   },
-  components: { Pagination, TableAction },
+  components: { Pagination, TableAction, DialogAssignment },
   created() {
     this.getStatusOptions();
     this.onClickSearch();
@@ -168,6 +192,22 @@ export default {
         path: "/marketing-management/campaign/create"
       })
     },
+    handleCommand(cb, item) {
+      this[cb](item)
+    },
+    assignSites(row) {
+      if (row) {
+        this.dialogAssign.item = row;
+        this.dialogAssign.visible = true;
+      }
+    },
+    hideAssignDialog(e) {
+      this.dialogAssign.item = {};
+      this.dialogAssign.visible = false;
+      if (e) {
+        this.getTableData();
+      }
+    },
     onClickEdit(row) {
       this.$router.push({
         path: "/marketing-management/campaign/update/" + row.campaignId

+ 1 - 0
Strides-Admin/src/views/site-label/assignment.vue

@@ -143,6 +143,7 @@ export default {
         console.log("watch.visible", n, o);
         if (n) {
           this.filter.pageVo.siteLabelId = this.item.siteLabelId;
+          this.filter.pageVo.assignmentStatus = "";
           this.onSearch()
         }
       }