936 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			936 lines
		
	
	
		
			27 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						||
  <div class="scheduling">
 | 
						||
    <!-- 左侧日程列表 -->
 | 
						||
    <a-card class="scheduling-list">
 | 
						||
      <!-- 标题 -->
 | 
						||
      <template slot="title">
 | 
						||
        Scheduling
 | 
						||
        <div class="line"></div>
 | 
						||
      </template>
 | 
						||
      <!-- 标题结束 -->
 | 
						||
      <!-- 内容 -->
 | 
						||
      <div class="scheduling-list-content">
 | 
						||
        <div class="scheduling-list-item" v-for="item of schedulingInfo" :key="item.id">
 | 
						||
          <h4 class="title">
 | 
						||
            <span>
 | 
						||
              {{ item.username }}
 | 
						||
            </span>
 | 
						||
            <a-popconfirm title="Do You Want To Delete This Item?" placement="bottom" @confirm="onDel(item)">
 | 
						||
              <a class="del"></a>
 | 
						||
            </a-popconfirm>
 | 
						||
          </h4>
 | 
						||
          <div class="scheduling-list-item-container" @dragover.prevent @drop="onDrop(item)">
 | 
						||
            <!-- 左侧边框 -->
 | 
						||
            <div class="left-border">
 | 
						||
              <div class="left-top-border">
 | 
						||
                <div class="left-top-corner"></div>
 | 
						||
              </div>
 | 
						||
              <div class="left-bottom-border"></div>
 | 
						||
            </div>
 | 
						||
            <!-- 左侧边框结束 -->
 | 
						||
            <div class="scheduling-list-item-content">
 | 
						||
              <template v-for="(station, index) in item.stationList">
 | 
						||
                <span
 | 
						||
                  :key="station.stationId"
 | 
						||
                  class="draggable"
 | 
						||
                  draggable
 | 
						||
                  @dragstart="onDragStart(station, item.userId)"
 | 
						||
                >
 | 
						||
                  {{ station.stationName }}
 | 
						||
                </span>
 | 
						||
                {{ index == item.stationList.length - 1 ? '' : '、' }}
 | 
						||
              </template>
 | 
						||
            </div>
 | 
						||
            <!-- 右侧边框 -->
 | 
						||
            <div class="right-border">
 | 
						||
              <div class="right-top-border"></div>
 | 
						||
              <div class="right-bottom-border"></div>
 | 
						||
            </div>
 | 
						||
            <!-- 右侧边框结束 -->
 | 
						||
          </div>
 | 
						||
        </div>
 | 
						||
        <a-empty v-if="!schedulingInfo || !schedulingInfo.length" style="margin-top: 40px;"></a-empty>
 | 
						||
      </div>
 | 
						||
      <!-- 内容结束 -->
 | 
						||
    </a-card>
 | 
						||
    <div class="scheduling-calendar">
 | 
						||
      <!-- 日历 -->
 | 
						||
      <a-calendar v-model="currentDate" @select="onSelectDate">
 | 
						||
        <template slot="headerRender">
 | 
						||
          <!-- 搜索栏 -->
 | 
						||
          <div class="search-form">
 | 
						||
            <a-row>
 | 
						||
              <a-col :span="6">
 | 
						||
                <a-form-model class="search-form-form">
 | 
						||
                  <a-form-model-item label="Month" style="marign-bottom: 0">
 | 
						||
                    <a-month-picker v-model="currentMonth" :allow-clear="false"></a-month-picker>
 | 
						||
                  </a-form-model-item>
 | 
						||
                </a-form-model>
 | 
						||
              </a-col>
 | 
						||
              <a-space class="btn-group">
 | 
						||
                <a-button @click="onAdd" type="primary">
 | 
						||
                  <img src="@/assets/images/global/add.png" alt="" />
 | 
						||
                  Add
 | 
						||
                </a-button>
 | 
						||
                <a-button @click="onEdit" type="primary">
 | 
						||
                  <img src="@/assets/images/global/edit.png" alt="" />
 | 
						||
                  Edit
 | 
						||
                </a-button>
 | 
						||
                <a-button type="primary">
 | 
						||
                  <img src="@/assets/images/global/import.png" alt="" />
 | 
						||
                  Import
 | 
						||
                </a-button>
 | 
						||
                <a-button type="primary">
 | 
						||
                  <img src="@/assets/images/global/export.png" alt="" />
 | 
						||
                  Export
 | 
						||
                </a-button>
 | 
						||
              </a-space>
 | 
						||
            </a-row>
 | 
						||
          </div>
 | 
						||
          <!-- 搜索栏结束 -->
 | 
						||
        </template>
 | 
						||
        <!-- 日历内部内容 -->
 | 
						||
        <template slot="dateCellRender" slot-scope="value">
 | 
						||
          <div
 | 
						||
            class="scheduling-calendar-content"
 | 
						||
            v-if="scheduleList[value.format(dateFormat)] && scheduleList[value.format(dateFormat)].length"
 | 
						||
          >
 | 
						||
            <div class="item" v-for="item in scheduleList[value.format(dateFormat)]" :key="item.id">
 | 
						||
              {{ `${item.username}(${item.stationList.length})` }}
 | 
						||
            </div>
 | 
						||
          </div>
 | 
						||
        </template>
 | 
						||
        <!-- 日历内部内容 -->
 | 
						||
      </a-calendar>
 | 
						||
      <!-- 日历结束 -->
 | 
						||
    </div>
 | 
						||
 | 
						||
    <!-- 增加/编辑排班弹窗 -->
 | 
						||
    <custom-modal :title="isAdd ? 'Add' : 'Edit'" :width="845" v-model="visible" :okHandler="submit">
 | 
						||
      <a-spin :spinning="isGettingDetail">
 | 
						||
        <div class="account-assign">
 | 
						||
          <a-transfer
 | 
						||
            :selectedKeys="selectedStationKeys"
 | 
						||
            :target-keys="targetKeys"
 | 
						||
            :render="item => item.title"
 | 
						||
            :operations="['Assign', 'Remove']"
 | 
						||
            :titles="['Particulate Station', 'Roster personnel']"
 | 
						||
            :show-select-all="false"
 | 
						||
            @change="onChange"
 | 
						||
            @selectChange="handleSelectChange"
 | 
						||
          >
 | 
						||
            <template slot="children" slot-scope="{ props: { direction, selectedKeys }, on: { itemSelect } }">
 | 
						||
              <!-- 左侧穿梭框中的树 -->
 | 
						||
              <a-tree
 | 
						||
                v-if="direction === 'left'"
 | 
						||
                blockNode
 | 
						||
                checkable
 | 
						||
                checkStrictly
 | 
						||
                defaultExpandAll
 | 
						||
                :checkedKeys="[...selectedKeys, ...targetKeys]"
 | 
						||
                :treeData="treeData"
 | 
						||
                @check="
 | 
						||
                  (_, props) => {
 | 
						||
                    onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect)
 | 
						||
                  }
 | 
						||
                "
 | 
						||
                @select="
 | 
						||
                  (_, props) => {
 | 
						||
                    onChecked(_, props, [...selectedKeys, ...targetKeys], itemSelect)
 | 
						||
                  }
 | 
						||
                "
 | 
						||
              />
 | 
						||
              <!-- 右侧穿梭框中的树 -->
 | 
						||
              <a-tree
 | 
						||
                v-if="direction === 'right'"
 | 
						||
                blockNode
 | 
						||
                checkStrictly
 | 
						||
                defaultExpandAll
 | 
						||
                :treeData="accountTreeData"
 | 
						||
                :selectedKeys.sync="rightAccountChildSelectedKeys"
 | 
						||
                @select="
 | 
						||
                  (_, props) => {
 | 
						||
                    onSelectAccount(_, props, itemSelect)
 | 
						||
                  }
 | 
						||
                "
 | 
						||
              />
 | 
						||
            </template>
 | 
						||
          </a-transfer>
 | 
						||
 | 
						||
          <!-- 穿梭框右上方搜索 -->
 | 
						||
          <div class="account-search">
 | 
						||
            <label>User Name</label>
 | 
						||
            <custom-popover-search
 | 
						||
              placeholder="Enter User Name"
 | 
						||
              :options="accountList"
 | 
						||
              :keyword.sync="keyword"
 | 
						||
              :remote-method="getAccountList"
 | 
						||
              @add="onAddToList"
 | 
						||
            ></custom-popover-search>
 | 
						||
          </div>
 | 
						||
          <!-- 穿梭框右上方搜索结束 -->
 | 
						||
        </div>
 | 
						||
      </a-spin>
 | 
						||
    </custom-modal>
 | 
						||
    <!-- 增加/编辑排班弹窗结束 -->
 | 
						||
  </div>
 | 
						||
</template>
 | 
						||
<script>
 | 
						||
import FormMixin from '@/mixins/FormMixin'
 | 
						||
import moment from 'moment'
 | 
						||
import { cloneDeep } from 'lodash'
 | 
						||
import { getAction } from '@/api/manage'
 | 
						||
import CustomPopoverSearch from '@/components/CustomPopoverSearch'
 | 
						||
import { deleteAction, postAction, putAction } from '../../api/manage'
 | 
						||
 | 
						||
const dateFormat = 'YYYY-MM-DD'
 | 
						||
const monthFormat = 'YYYY-MM'
 | 
						||
export default {
 | 
						||
  mixins: [FormMixin],
 | 
						||
  components: {
 | 
						||
    VNodes: {
 | 
						||
      functional: true,
 | 
						||
      render: (_, ctx) => ctx.props.vnodes
 | 
						||
    },
 | 
						||
    CustomPopoverSearch
 | 
						||
  },
 | 
						||
  data() {
 | 
						||
    this.dateFormat = dateFormat
 | 
						||
    return {
 | 
						||
      currentMonth: moment(), // 当前月份
 | 
						||
 | 
						||
      currentDate: moment(), // 当前日期
 | 
						||
      schedulingInfo: [], // 当前选中的日程详情,用于 scheduling 左侧展示
 | 
						||
 | 
						||
      isGettingList: false,
 | 
						||
      scheduleList: [], // 该月的日程列表
 | 
						||
 | 
						||
      visible: false, // 新增/编辑弹窗是否显示
 | 
						||
 | 
						||
      isGettingDetail: false, // 编辑时正在获取详情
 | 
						||
 | 
						||
      originalTreeData: [], // 站点的树状结构
 | 
						||
      selectedStationKeys: [], // 穿梭框左侧选中的站点列表
 | 
						||
      targetKeys: [],
 | 
						||
 | 
						||
      isGettingStationList: false,
 | 
						||
      keyword: '', // 穿梭框右上角搜索用户的关键词
 | 
						||
 | 
						||
      stationList: [], // 站点列表(原始数据)
 | 
						||
      accountList: [], // 搜索出来的用户列表
 | 
						||
 | 
						||
      accountTreeData: [], // 用户列表树状结构
 | 
						||
      checkedAccount: '', // 右侧穿梭框选中的账号
 | 
						||
      rightAccountChildSelectedKeys: [], // 右侧穿梭框选中的值
 | 
						||
 | 
						||
      isChanging: false,
 | 
						||
      dragItem: null,
 | 
						||
      fromUserId: ''
 | 
						||
    }
 | 
						||
  },
 | 
						||
  created() {
 | 
						||
    this.getList()
 | 
						||
    this.getStationList()
 | 
						||
  },
 | 
						||
  methods: {
 | 
						||
    // 获取该月日程列表
 | 
						||
    async getList() {
 | 
						||
      try {
 | 
						||
        this.isGettingList = true
 | 
						||
        const { result } = await getAction(`/sysTask/findList?yearMonth=${this.currentMonth.format('YYYY-MM')}`)
 | 
						||
        this.scheduleList = result
 | 
						||
        this.schedulingInfo = this.scheduleList[this.currentDate.format(dateFormat)]
 | 
						||
      } catch (error) {
 | 
						||
        console.error(error)
 | 
						||
      } finally {
 | 
						||
        this.isGettingList = false
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    async getStationList() {
 | 
						||
      try {
 | 
						||
        this.isGettingStationList = true
 | 
						||
        const { success, result, message } = await getAction('/gardsStations/findPage?pageIndex=1&pageSize=1000')
 | 
						||
        if (success) {
 | 
						||
          const records = result.records
 | 
						||
          const set = new Set(records.map(item => item.countryCode))
 | 
						||
          this.originalTreeData = Array.from(set).map((countryCode, index) => {
 | 
						||
            return {
 | 
						||
              title: countryCode,
 | 
						||
              key: index.toString(),
 | 
						||
              disabled: true,
 | 
						||
              children: records
 | 
						||
                .filter(item => item.countryCode == countryCode)
 | 
						||
                .map(item => ({ title: item.stationCode, key: item.stationId.toString(), children: [] }))
 | 
						||
            }
 | 
						||
          })
 | 
						||
 | 
						||
          this.stationList = records
 | 
						||
        } else {
 | 
						||
          this.$message.error(message)
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        console.error(error)
 | 
						||
      } finally {
 | 
						||
        this.isGettingStationList = false
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    async getAccountList() {
 | 
						||
      try {
 | 
						||
        const { success, result, message } = await getAction(
 | 
						||
          `/sys/user/list?pageIndex=1&pageSize=1000&username=${this.keyword}`
 | 
						||
        )
 | 
						||
        if (success) {
 | 
						||
          const records = result.records
 | 
						||
          this.accountList = records.map(item => ({
 | 
						||
            label: item.username,
 | 
						||
            value: item.id
 | 
						||
          }))
 | 
						||
        } else {
 | 
						||
          this.$message.error(message)
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        console.error(error)
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 选中某一个日期
 | 
						||
    onSelectDate(date) {
 | 
						||
      this.currentDate = date
 | 
						||
      if (this.currentDate.format(monthFormat) !== this.currentMonth.format(monthFormat)) {
 | 
						||
        this.currentMonth = this.currentDate.clone()
 | 
						||
      }
 | 
						||
      if (this.isGettingList) {
 | 
						||
        this.$message.info('Is Getting Schedule,Waitting...')
 | 
						||
        return
 | 
						||
      }
 | 
						||
      this.schedulingInfo = this.scheduleList[date.format(dateFormat)]
 | 
						||
    },
 | 
						||
 | 
						||
    // 获取详情
 | 
						||
    async getScheduleDetail() {
 | 
						||
      try {
 | 
						||
        this.isGettingDetail = true
 | 
						||
        const { success, result, message } = await getAction(
 | 
						||
          `/sysTask/findInfo?day=${this.currentDate.format(dateFormat)}`
 | 
						||
        )
 | 
						||
        if (success) {
 | 
						||
          const selectedStations = [] // 找出已经被选中的站点
 | 
						||
          const accountTree = [] // 构建右侧的树
 | 
						||
          result.forEach(item => {
 | 
						||
            const accountItem = {
 | 
						||
              title: item.userName,
 | 
						||
              key: item.userId,
 | 
						||
              children: []
 | 
						||
            }
 | 
						||
            accountTree.push(accountItem)
 | 
						||
 | 
						||
            item.stationList.forEach(station => {
 | 
						||
              selectedStations.push(station.stationId)
 | 
						||
              accountItem.children.push({
 | 
						||
                title: station.stationName,
 | 
						||
                key: station.stationId,
 | 
						||
                isLeaf: true
 | 
						||
              })
 | 
						||
            })
 | 
						||
          })
 | 
						||
          this.targetKeys = selectedStations
 | 
						||
          this.accountTreeData = accountTree
 | 
						||
        } else {
 | 
						||
          this.$message.error(message)
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        console.error(error)
 | 
						||
      } finally {
 | 
						||
        this.isGettingDetail = false
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 左侧删除某一天的日程
 | 
						||
    async onDel({ id: taskId, userId }) {
 | 
						||
      try {
 | 
						||
        const { success } = await deleteAction('/sysTask/deleteById', {
 | 
						||
          taskId,
 | 
						||
          userId
 | 
						||
        })
 | 
						||
        if (success) {
 | 
						||
          this.$message.success('Delete Success')
 | 
						||
          this.getList()
 | 
						||
        } else {
 | 
						||
          this.$message.success('Delete Fail')
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        console.error(error)
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    onAdd() {
 | 
						||
      if (this.schedulingInfo.length) {
 | 
						||
        this.$message.warn('Has Schedule, Only Can Edit')
 | 
						||
        return
 | 
						||
      }
 | 
						||
 | 
						||
      this.isAdd = true
 | 
						||
      this.visible = true
 | 
						||
 | 
						||
      this.selectedStationKeys = []
 | 
						||
      this.targetKeys = []
 | 
						||
      this.accountTreeData = []
 | 
						||
      this.rightAccountChildSelectedKeys = []
 | 
						||
      this.checkedAccount = ''
 | 
						||
    },
 | 
						||
 | 
						||
    onEdit() {
 | 
						||
      if (!this.schedulingInfo.length) {
 | 
						||
        this.$message.warn('Has No Schedule, Add It First')
 | 
						||
        return
 | 
						||
      }
 | 
						||
      this.isAdd = false
 | 
						||
      this.visible = true
 | 
						||
 | 
						||
      this.rightAccountChildSelectedKeys = []
 | 
						||
      this.checkedAccount = ''
 | 
						||
      this.getScheduleDetail()
 | 
						||
    },
 | 
						||
 | 
						||
    onImport() {
 | 
						||
      console.log('%c [ 新增 ]-88', 'font-size:13px; background:pink; color:#bf2c9f;')
 | 
						||
    },
 | 
						||
 | 
						||
    onExport() {
 | 
						||
      console.log('%c [ 新增 ]-88', 'font-size:13px; background:pink; color:#bf2c9f;')
 | 
						||
    },
 | 
						||
 | 
						||
    // 开始拖拽
 | 
						||
    onDragStart(item, fromUserId) {
 | 
						||
      this.dragItem = item
 | 
						||
      this.fromUserId = fromUserId
 | 
						||
    },
 | 
						||
    // 拖拽结束
 | 
						||
    async onDrop(item) {
 | 
						||
      const toUserId = item.userId
 | 
						||
      if (this.fromUserId != toUserId) {
 | 
						||
        try {
 | 
						||
          this.isChanging = true
 | 
						||
          const { success } = await putAction('/sysTask/changeScheduling', {
 | 
						||
            day: this.currentDate.format('YYYY-MM-DD'),
 | 
						||
            fromUserId: this.fromUserId,
 | 
						||
            toUserId,
 | 
						||
            stationIds: [this.dragItem.stationId]
 | 
						||
          })
 | 
						||
          if (success) {
 | 
						||
            this.$message.success('Change Success')
 | 
						||
            this.getList()
 | 
						||
          } else {
 | 
						||
            this.$message.error('Change Fail')
 | 
						||
          }
 | 
						||
        } catch (error) {
 | 
						||
          console.error()
 | 
						||
        } finally {
 | 
						||
          this.isChanging = false
 | 
						||
        }
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 提交日程
 | 
						||
    async submit() {
 | 
						||
      if (!this.accountTreeData.length) {
 | 
						||
        this.$message.warn('Person List Is Empty')
 | 
						||
        return Promise.reject()
 | 
						||
      }
 | 
						||
      if (!this.accountTreeData.some(accountTreeItem => accountTreeItem.children.length)) {
 | 
						||
        this.$message.warn('Every Person Has No Station Assigned')
 | 
						||
        return Promise.reject()
 | 
						||
      }
 | 
						||
      try {
 | 
						||
        let method = putAction
 | 
						||
        let url = '/sysTask/update'
 | 
						||
        let successMsg = 'Edit Success'
 | 
						||
        let failMsg = 'Edit Fail'
 | 
						||
        if (this.isAdd) {
 | 
						||
          method = postAction
 | 
						||
          url = '/sysTask/create'
 | 
						||
          successMsg = 'Add Success'
 | 
						||
          failMsg = 'Add Fail'
 | 
						||
        }
 | 
						||
        const params = []
 | 
						||
        this.accountTreeData.forEach(accountTreeItem => {
 | 
						||
          if (accountTreeItem.children.length) {
 | 
						||
            params.push({
 | 
						||
              schedulingDate: this.currentDate.format('YYYY-MM-DD HH:mm:ss'),
 | 
						||
              stationList: accountTreeItem.children.map(child => {
 | 
						||
                return {
 | 
						||
                  stationId: child.key
 | 
						||
                }
 | 
						||
              }),
 | 
						||
              userId: accountTreeItem.key
 | 
						||
            })
 | 
						||
          }
 | 
						||
        })
 | 
						||
        const { success } = await method(url, params)
 | 
						||
        if (success) {
 | 
						||
          this.$message.success(successMsg)
 | 
						||
          this.getList()
 | 
						||
        } else {
 | 
						||
          this.$message.error(failMsg)
 | 
						||
          return Promise.reject()
 | 
						||
        }
 | 
						||
      } catch (error) {
 | 
						||
        return Promise.reject()
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    /**
 | 
						||
     * 以下是对穿梭框的处理
 | 
						||
     */
 | 
						||
 | 
						||
    isChecked(selectedKeys, eventKey) {
 | 
						||
      return selectedKeys.indexOf(eventKey) !== -1
 | 
						||
    },
 | 
						||
 | 
						||
    onChecked(_, e, checkedKeys, itemSelect) {
 | 
						||
      const { eventKey } = e.node
 | 
						||
      itemSelect(eventKey, !this.isChecked(checkedKeys, eventKey))
 | 
						||
    },
 | 
						||
 | 
						||
    handleSelectChange(sourceSelectedKeys, targetSelectedKeys) {
 | 
						||
      this.selectedStationKeys = [...sourceSelectedKeys, ...targetSelectedKeys]
 | 
						||
    },
 | 
						||
 | 
						||
    // 处理station列表
 | 
						||
    handleTreeData(data, targetKeys = [], level) {
 | 
						||
      data.forEach(item => {
 | 
						||
        if (level !== 0) {
 | 
						||
          item.disabled = targetKeys.includes(item.key)
 | 
						||
        }
 | 
						||
        if (item.children) {
 | 
						||
          this.handleTreeData(item.children, targetKeys)
 | 
						||
        }
 | 
						||
      })
 | 
						||
      return data
 | 
						||
    },
 | 
						||
 | 
						||
    // 将选中的账号加入到右侧穿梭框
 | 
						||
    onAddToList(selectedAccount) {
 | 
						||
      const addedAccounts = selectedAccount
 | 
						||
        .filter(id => !this.accountTreeData.find(account => account.key == id)) // 如果之前没有加过,才加进去
 | 
						||
        .map(id => {
 | 
						||
          const find = this.accountList.find(account => account.value == id)
 | 
						||
          return {
 | 
						||
            title: find.label,
 | 
						||
            key: id,
 | 
						||
            children: []
 | 
						||
          }
 | 
						||
        })
 | 
						||
      this.accountTreeData.push(...addedAccounts)
 | 
						||
      this.rightAccountChildSelectedKeys = []
 | 
						||
      this.checkedAccount = ''
 | 
						||
    },
 | 
						||
 | 
						||
    // 选中了穿梭框右侧的树节点
 | 
						||
    onSelectAccount(_, e, itemSelect) {
 | 
						||
      const { eventKey, isLeaf, selected } = e.node
 | 
						||
      // selected 是前一个状态,也就是selected 为false时,其实是选中了
 | 
						||
      if (isLeaf) {
 | 
						||
        // 选中了子节点,也就是站点
 | 
						||
        this.checkedAccount = ''
 | 
						||
 | 
						||
        if (selected) {
 | 
						||
          // 取消选中
 | 
						||
          itemSelect(eventKey, false)
 | 
						||
        } else {
 | 
						||
          this.accountTreeData.forEach(account => {
 | 
						||
            // 将所有右侧的穿梭状态重置
 | 
						||
            account.children.forEach(child => {
 | 
						||
              itemSelect(child.key, false)
 | 
						||
            })
 | 
						||
          })
 | 
						||
          itemSelect(eventKey, true) // 选中该栏
 | 
						||
        }
 | 
						||
      } else {
 | 
						||
        this.checkedAccount = selected ? '' : eventKey
 | 
						||
        // 将所有右侧的穿梭状态重置
 | 
						||
        this.accountTreeData.forEach(account => {
 | 
						||
          // 将所有右侧的穿梭状态重置
 | 
						||
          account.children.forEach(child => {
 | 
						||
            itemSelect(child.key, false)
 | 
						||
          })
 | 
						||
        })
 | 
						||
      }
 | 
						||
    },
 | 
						||
 | 
						||
    // 穿梭框变化
 | 
						||
    onChange(targetKeys, direction, moveKeys) {
 | 
						||
      if (direction == 'right') {
 | 
						||
        if (!this.checkedAccount) {
 | 
						||
          this.$message.warning('Please Select A Person To Assign')
 | 
						||
          return
 | 
						||
        }
 | 
						||
        const findAccount = this.accountTreeData.find(account => account.key == this.checkedAccount)
 | 
						||
        if (findAccount) {
 | 
						||
          const children = moveKeys.map(key => {
 | 
						||
            const findStation = this.stationList.find(station => station.stationId == key)
 | 
						||
            return {
 | 
						||
              isLeaf: true,
 | 
						||
              title: findStation.stationCode,
 | 
						||
              key
 | 
						||
            }
 | 
						||
          })
 | 
						||
          findAccount.children.push(...children)
 | 
						||
        }
 | 
						||
      } else {
 | 
						||
        const moveKey = moveKeys[0]
 | 
						||
        console.log('%c [ moveKey ]-577', 'font-size:13px; background:pink; color:#bf2c9f;', moveKey)
 | 
						||
        let parentIndex = -1,
 | 
						||
          childIndex = -1
 | 
						||
        for (const pIndex in this.accountTreeData) {
 | 
						||
          // 找到要移除的Station在右侧列表中的位置
 | 
						||
          const account = this.accountTreeData[pIndex]
 | 
						||
          const cIndex = account.children.findIndex(child => child.key == moveKey)
 | 
						||
          if (-1 !== cIndex) {
 | 
						||
            parentIndex = pIndex
 | 
						||
            childIndex = cIndex
 | 
						||
            break
 | 
						||
          }
 | 
						||
        }
 | 
						||
        this.accountTreeData[parentIndex].children.splice(childIndex, 1)
 | 
						||
        console.log('%c [ childIndex ]-591', 'font-size:13px; background:pink; color:#bf2c9f;', childIndex)
 | 
						||
      }
 | 
						||
      this.targetKeys = targetKeys
 | 
						||
    }
 | 
						||
  },
 | 
						||
  computed: {
 | 
						||
    treeData() {
 | 
						||
      return this.handleTreeData(cloneDeep(this.originalTreeData), this.targetKeys, 0)
 | 
						||
    }
 | 
						||
  },
 | 
						||
  watch: {
 | 
						||
    currentMonth() {
 | 
						||
      this.currentDate = this.currentMonth.clone()
 | 
						||
      this.getList()
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
</script>
 | 
						||
<style lang="less" scoped>
 | 
						||
.scheduling {
 | 
						||
  height: 100%;
 | 
						||
  overflow: hidden;
 | 
						||
  display: flex;
 | 
						||
  &-list {
 | 
						||
    flex-shrink: 0;
 | 
						||
    width: 350px;
 | 
						||
    height: 100%;
 | 
						||
    background-color: #022024;
 | 
						||
    border-color: rgb(12, 106, 102, 0.9);
 | 
						||
    border-radius: 0;
 | 
						||
    ::v-deep {
 | 
						||
      .ant-card {
 | 
						||
        &-head {
 | 
						||
          font-family: MicrogrammaD-MediExte;
 | 
						||
          font-size: 20px;
 | 
						||
          font-weight: bold;
 | 
						||
          padding-left: 15px;
 | 
						||
          background-color: #022024;
 | 
						||
          border-bottom-color: rgba(12, 235, 201, 0.3);
 | 
						||
          &-title {
 | 
						||
            padding-top: 22px;
 | 
						||
            padding-bottom: 0;
 | 
						||
          }
 | 
						||
          .line {
 | 
						||
            width: 70px;
 | 
						||
            height: 3px;
 | 
						||
            background-color: #0cebc9;
 | 
						||
            margin-top: 10px;
 | 
						||
          }
 | 
						||
        }
 | 
						||
        &-body {
 | 
						||
          height: calc(100% - 66px);
 | 
						||
          overflow: auto;
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
    &-content {
 | 
						||
      padding: 0 12px;
 | 
						||
    }
 | 
						||
    &-item {
 | 
						||
      margin-top: 15px;
 | 
						||
      .title {
 | 
						||
        display: flex;
 | 
						||
        justify-content: space-between;
 | 
						||
        align-items: center;
 | 
						||
        font-family: Aria;
 | 
						||
        font-size: 14px;
 | 
						||
        color: #fff;
 | 
						||
        font-weight: bold;
 | 
						||
        margin-bottom: 4px;
 | 
						||
        position: relative;
 | 
						||
        padding-left: 8px;
 | 
						||
        span {
 | 
						||
          flex: 1;
 | 
						||
          overflow: hidden;
 | 
						||
          white-space: nowrap;
 | 
						||
          text-overflow: ellipsis;
 | 
						||
        }
 | 
						||
        .del {
 | 
						||
          margin-left: 15px;
 | 
						||
          margin-right: 10px;
 | 
						||
          width: 14px;
 | 
						||
          height: 14px;
 | 
						||
          background: url(~@/assets/images/system/del-normal.png);
 | 
						||
          background-size: cover;
 | 
						||
          &:hover {
 | 
						||
            background-image: url(~@/assets/images/system/del-active.png);
 | 
						||
          }
 | 
						||
        }
 | 
						||
      }
 | 
						||
      @borderColor: #0a544e;
 | 
						||
      @borderRadius: 4px;
 | 
						||
      @bgColor: #03353f;
 | 
						||
      &-container {
 | 
						||
        border-bottom: 1px solid @borderColor;
 | 
						||
        border-bottom-left-radius: @borderRadius;
 | 
						||
        border-bottom-right-radius: @borderRadius;
 | 
						||
        margin-top: 4px;
 | 
						||
        display: flex;
 | 
						||
        .left-border {
 | 
						||
          width: 6px;
 | 
						||
          position: relative;
 | 
						||
          .left-top-border {
 | 
						||
            position: relative;
 | 
						||
            .left-top-corner {
 | 
						||
              border: 1px solid @borderColor;
 | 
						||
              background-color: @bgColor;
 | 
						||
              position: absolute;
 | 
						||
              top: 25px;
 | 
						||
              left: 2px;
 | 
						||
              width: 9px;
 | 
						||
              height: 9px;
 | 
						||
              transform: rotate(45deg);
 | 
						||
              border-top: none;
 | 
						||
              border-right: 0;
 | 
						||
            }
 | 
						||
          }
 | 
						||
          .left-bottom-border {
 | 
						||
            height: calc(100% - 30px);
 | 
						||
            position: absolute;
 | 
						||
            left: 0;
 | 
						||
            bottom: 0;
 | 
						||
            width: 7px;
 | 
						||
            background-color: @bgColor;
 | 
						||
            border-left: 1px solid @borderColor;
 | 
						||
            border-bottom-left-radius: @borderRadius;
 | 
						||
          }
 | 
						||
        }
 | 
						||
        .right-border {
 | 
						||
          width: 14px;
 | 
						||
          position: relative;
 | 
						||
          .right-top-border {
 | 
						||
            position: absolute;
 | 
						||
            right: 4px;
 | 
						||
            top: 4px;
 | 
						||
            width: 20px;
 | 
						||
            height: 20px;
 | 
						||
            border: 1px solid transparent;
 | 
						||
            border-top-color: @borderColor;
 | 
						||
            transform: rotate(45deg);
 | 
						||
            background-color: @bgColor;
 | 
						||
          }
 | 
						||
 | 
						||
          .right-bottom-border {
 | 
						||
            position: absolute;
 | 
						||
            right: 0;
 | 
						||
            bottom: 0;
 | 
						||
            width: 100%;
 | 
						||
            height: calc(100% - 14px);
 | 
						||
            border-right: 1px solid @borderColor;
 | 
						||
            border-bottom-right-radius: @borderRadius;
 | 
						||
            background-color: @bgColor;
 | 
						||
          }
 | 
						||
        }
 | 
						||
      }
 | 
						||
      &-content {
 | 
						||
        min-height: 62px;
 | 
						||
        border-top: 1px solid @borderColor;
 | 
						||
        border-left: 1px solid @borderColor;
 | 
						||
        padding: 6px;
 | 
						||
        background-color: @bgColor;
 | 
						||
        line-height: 26px;
 | 
						||
        flex: 1;
 | 
						||
        color: #6ebad0;
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
 | 
						||
  // 右侧日历
 | 
						||
  &-calendar {
 | 
						||
    height: 100%;
 | 
						||
    margin-left: 20px;
 | 
						||
    padding-right: 10px;
 | 
						||
    .ant-fullcalendar-fullscreen {
 | 
						||
      height: 100%;
 | 
						||
      overflow: auto;
 | 
						||
      padding-right: 5px;
 | 
						||
      ::v-deep {
 | 
						||
        .ant-fullcalendar-full {
 | 
						||
          height: calc(100% - 61px);
 | 
						||
          overflow: auto;
 | 
						||
          padding-right: 5px;
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
    .search-form {
 | 
						||
      padding: 10px;
 | 
						||
      border: 1px solid #416f7f;
 | 
						||
      border-bottom: 0;
 | 
						||
      &-form {
 | 
						||
        .ant-form-item {
 | 
						||
          margin-bottom: 0;
 | 
						||
        }
 | 
						||
      }
 | 
						||
      .btn-group {
 | 
						||
        float: right;
 | 
						||
        height: 40px;
 | 
						||
        img {
 | 
						||
          margin-right: 12px;
 | 
						||
          height: 18px;
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
 | 
						||
    &-content {
 | 
						||
      padding: 10px;
 | 
						||
      height: 100%;
 | 
						||
      overflow: hidden auto;
 | 
						||
      background-color: rgba(140, 255, 229, 0.1);
 | 
						||
      &::-webkit-scrollbar {
 | 
						||
        width: 4px !important;
 | 
						||
      }
 | 
						||
      .item {
 | 
						||
        font-family: Arial;
 | 
						||
        font-size: 14px;
 | 
						||
        color: #00fdd3;
 | 
						||
        height: 20px;
 | 
						||
        overflow: hidden;
 | 
						||
        text-overflow: ellipsis;
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.account-assign {
 | 
						||
  position: relative;
 | 
						||
  width: 672px;
 | 
						||
  margin: 0 auto;
 | 
						||
  .ant-transfer {
 | 
						||
    margin-bottom: 10px;
 | 
						||
    ::v-deep {
 | 
						||
      .ant-transfer-list {
 | 
						||
        width: 282px;
 | 
						||
        height: 411px;
 | 
						||
        &-header {
 | 
						||
          height: 37px;
 | 
						||
          &-selected {
 | 
						||
            span:first-child {
 | 
						||
              display: none;
 | 
						||
            }
 | 
						||
          }
 | 
						||
          &-title {
 | 
						||
            left: 16px;
 | 
						||
          }
 | 
						||
        }
 | 
						||
        &-content {
 | 
						||
          &-item {
 | 
						||
            &:hover {
 | 
						||
              background-color: transparent;
 | 
						||
            }
 | 
						||
          }
 | 
						||
        }
 | 
						||
 | 
						||
        &:last-child {
 | 
						||
          height: 364px;
 | 
						||
          position: relative;
 | 
						||
          top: 47px;
 | 
						||
        }
 | 
						||
      }
 | 
						||
 | 
						||
      .ant-transfer-operation {
 | 
						||
        .ant-btn {
 | 
						||
          width: 92px;
 | 
						||
          height: 26px;
 | 
						||
          padding: 0;
 | 
						||
          .anticon {
 | 
						||
            display: none;
 | 
						||
          }
 | 
						||
          span {
 | 
						||
            margin-left: 0;
 | 
						||
          }
 | 
						||
          &:first-child {
 | 
						||
            margin-bottom: 52px;
 | 
						||
            &::after {
 | 
						||
              display: inline-block;
 | 
						||
              margin-left: 13px;
 | 
						||
              content: '';
 | 
						||
              width: 18px;
 | 
						||
              height: 10px;
 | 
						||
              background: url(~@/assets/images/system/transfer-right.png) no-repeat;
 | 
						||
              background-size: contain;
 | 
						||
            }
 | 
						||
          }
 | 
						||
          &:nth-child(2) {
 | 
						||
            &::before {
 | 
						||
              display: inline-block;
 | 
						||
              margin-right: 6px;
 | 
						||
              content: '';
 | 
						||
              width: 18px;
 | 
						||
              height: 10px;
 | 
						||
              background: url(~@/assets/images/system/transfer-left.png) no-repeat;
 | 
						||
              background-size: contain;
 | 
						||
              position: static;
 | 
						||
              opacity: initial;
 | 
						||
            }
 | 
						||
          }
 | 
						||
        }
 | 
						||
      }
 | 
						||
    }
 | 
						||
  }
 | 
						||
  .account-search {
 | 
						||
    position: absolute;
 | 
						||
    top: 0;
 | 
						||
    right: 0;
 | 
						||
    width: 282px;
 | 
						||
    display: flex;
 | 
						||
    align-items: center;
 | 
						||
    label {
 | 
						||
      color: #5b9cba;
 | 
						||
      font-size: 16px;
 | 
						||
      flex-shrink: 0;
 | 
						||
      margin-right: 10px;
 | 
						||
      user-select: none;
 | 
						||
    }
 | 
						||
  }
 | 
						||
}
 | 
						||
.ant-select-dropdown-content::before {
 | 
						||
  top: 2px;
 | 
						||
}
 | 
						||
.account-add {
 | 
						||
  padding: 0;
 | 
						||
  padding-top: 10px;
 | 
						||
  text-align: center;
 | 
						||
  cursor: pointer;
 | 
						||
  background: #03353f;
 | 
						||
  a {
 | 
						||
    display: inline-block;
 | 
						||
    width: 100%;
 | 
						||
    padding: 5px;
 | 
						||
    border-top: 1px solid #0da397;
 | 
						||
    color: #0cebc9;
 | 
						||
  }
 | 
						||
}
 | 
						||
 | 
						||
.draggable {
 | 
						||
  cursor: pointer;
 | 
						||
}
 | 
						||
</style>
 |