ll
liukangdong
2025-02-20 b565a275fe240e2bd8a939c3b4beed98d03767bc
ll
已添加6个文件
已修改19个文件
1552 ■■■■ 文件已修改
admin/public/template/tram_import_tem.xlsx 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/bikes.js 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/combo.js 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/assets/style/style.scss 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/common/GlobalWindow.vue 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/utils/request.js 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/bikeRepair.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/bikeRetakeRecord.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/components/Map.vue 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/goodsorder.vue 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/memberRides.vue 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/miniproSetting.vue 123 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/onlinePayStatistics.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/combo/components/ComboDetail.vue 81 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/combo/components/Edit.vue 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/combo/components/Refund.vue 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/combo/components/config.js 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/combo/index.vue 100 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/combo/order.vue 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/operation/components/AMap.vue 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/operation/components/QRcode.vue 122 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/operation/components/SitesWindow.vue 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/operation/components/TramEdit.vue 46 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/operation/site.vue 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/operation/tram.vue 201 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/public/template/tram_import_tem.xlsx
Binary files differ
admin/src/api/business/bikes.js
@@ -1,23 +1,47 @@
import request from '../../utils/request'
// æŸ¥è¯¢
export function fetchList (data) {
export function fetchList(data) {
  return request.post('/business/bikes/page', data, {
    trim: true
  })
}
export function tranListPost(data) {
  return request.post('/business/bikes/pageCurrent', data, {
    trim: true
  })
}
export function baseTranListPost(params) {
  return request.get('/business/baseParam/baseParamList', { params })
}
// äºŒç»´ç åˆ—表
export function qrcodeBike(params) {
  return request.get(`/business/locks/findBikeBase`, { params })
}
export function qrcodeBikeList(params) {
  return request.get(`/business/locks/findAllBikeBase`, { params })
}
export function downloadImgs(data) {
  return request.post(`/business/locks/exportEbikeImages`, data, {
    trim: true,
    download: true
  })
}
// åˆ›å»º
export function create (data) {
export function create(data) {
  return request.post('/business/bikes/create', data)
}
// ä¿®æ”¹
export function updateById (data) {
export function updateById(data) {
  return request.post('/business/bikes/updateById', data)
}
// åˆ é™¤
export function deleteById (id) {
export function deleteById(id) {
  return request.get(`/business/bikes/delete/${id}`)
}
// åˆ é™¤
export function deleteBatchById(params) {
  return request.get(`/business/bikes/delete/batch`, { params })
}
admin/src/api/business/combo.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
import request from '@/utils/request'
export function comboAddPost(data) {
  return request.post('/business/discount/create', data, {
    trim: true
  })
}
export function comboEditPost(data) {
  return request.post('/business/discount/updateById', data, {
    trim: true
  })
}
export function comboListPost(data) {
  return request.post('/business/discount/page', data, {
    trim: true
  })
}
export function comboDetailPost(id) {
  return request.get('/business/discount/' + id, {
    trim: true
  })
}
export function comboOrderPost(data) {
  return request.post('/business/goodsorder/page', data, {
    trim: true
  })
}
// å¯¼å‡ºExcel
export function comboOrderEx(data) {
  return request.post('/business/goodsorder/exportExcel', data, {
    trim: true,
    download: true
  })
}
export function comboReDetailPost(params) {
  return request.get('/business/goodsorder/getGoodsorderCanBanlanceDTO', {params})
}
export function comboOrderRefundPost(data) {
  return request.post('/business/goodsorder/backGoodsorder', data, {
    trim: true
  })
}
export function comboOrderDetailPost(params) {
  return request.get('/business/goodsorder/discountOrderDetail', {params})
}
export function comboListOrderEx(data) {
  return request.post('/business/goodsorder/discountExportExcel', data, {
    trim: true,
    download: true
  })
}
admin/src/assets/style/style.scss
@@ -60,6 +60,9 @@
.primaryColor{
  color: $primary-color;
}
.red{
  color: red;
}
.pointer{
  cursor: pointer;
}
admin/src/components/common/GlobalWindow.vue
@@ -62,6 +62,7 @@
    },
    close () {
      this.$emit('update:visible', false)
      this.$emit('close')
    }
  }
}
admin/src/utils/request.js
@@ -41,7 +41,7 @@
// æ–°å»ºå“åº”拦截器
axiosInstance.interceptors.response.use((response) => {
  // console.log(response.status)
  // debugger
  // è¯·æ±‚失败
@@ -62,9 +62,15 @@
  }
  // ä¸šåŠ¡å¤±è´¥
  if (!response.data.success) {
    return Promise.reject(response.data)
    Message.error(response.data.message)
    return null
  }
  return response.data.data
  if (response.data.data) {
    return response.data.data
  } else {
    return response.data
  }
}, function (error) {
  if (error.code == null) {
    return Promise.reject(new Error('服务器繁忙,请稍后再试'))
@@ -82,7 +88,7 @@
    __arguments: args,
    __result_promise: null,
    // å¼€å¯ç¼“å­˜
    cache () {
    cache() {
      const data = this.__cacheImpl.getJSON(cacheKey)
      if (data != null) {
        this.__result_promise = Promise.resolve(data)
@@ -92,16 +98,16 @@
      }
      return this
    },
    then () {
    then() {
      return this.__access('then', arguments)
    },
    catch () {
    catch() {
      return this.__access('catch', arguments)
    },
    finally () {
    finally() {
      return this.__access('finally', arguments)
    },
    __access (methodName, args) {
    __access(methodName, args) {
      if (this.__result_promise != null) {
        return this.__result_promise
      }
admin/src/views/business/bikeRepair.vue
@@ -150,7 +150,7 @@
      'field.id': 'id',
      'field.main': 'id'
    })
    this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    // this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    this.search()
  },
  methods: {
admin/src/views/business/bikeRetakeRecord.vue
@@ -165,8 +165,8 @@
      'field.id': 'id',
      'field.main': 'id'
    })
    this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    this.searchForm.endDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 23:59:59'
    // this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    // this.searchForm.endDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 23:59:59'
    this.search()
  },
  methods: {
admin/src/views/business/components/Map.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,144 @@
<template>
  <GlobalWindow title="绘制运营区域" :visible.sync="isShowModal" :confirm-working="isWorking" @close="close"
    @confirm="confirm" width="1000px">
    <div class="map_title">绘制:鼠标左键点击及移动即可绘制图形,绘制过程中按下esc键可中断该过程</div>
    <div class="map_title">鼠标左键双击即可结束当前多边形绘制,多边形会自动闭合;</div>
    <div class="map_wrap">
      <div class="btns">
        <el-button plain type="primary" @click="reset">{{ polygons.length > 0 ? '重新绘制' : '开始绘制' }}</el-button>
        <!-- <el-button plain type="primary" @click="changeMode">结束绘制</el-button> -->
        <!-- <el-button plain @click="reset">重新绘制</el-button> -->
        <!-- <el-button plain  type="danger" @click="deleteObj">删除</el-button> -->
      </div>
      <div id="container" style="width: 100%; height: 100%"></div>
    </div>
  </GlobalWindow>
</template>
<script>
import GlobalWindow from '@/components/common/GlobalWindow'
import { Message } from 'element-ui'
var map // åœ°å›¾
// var editor // ç¼–辑器
export default {
  components: {
    GlobalWindow
  },
  data() {
    return {
      isShowModal: false,
      isWorking: false,
      map: null,
      editor: null,
      polygon: null,
      polygons: [],            // å­˜å‚¨æ‰€æœ‰ç»˜åˆ¶çš„多边形
      selectedPolygon: null,
    }
  },
  methods: {
    initMap() {
      map = new TMap.Map("container", {
        zoom: 15, // è®¾ç½®åœ°å›¾ç¼©æ”¾çº§åˆ«
        center: new TMap.LatLng(31.722763, 117.385480) // è®¾ç½®åœ°å›¾ä¸­å¿ƒç‚¹åæ ‡
      })
      var tempList = []
      if (this.polygons.length > 0) {
        tempList = [{ paths: this.polygons.map(item => new TMap.LatLng(item.lat, item.lng)) }]
      }
      this.polygon = new TMap.MultiPolygon({
        map: map,
        id: 'polygon',
        selectedStyleId: 'highlight',
        styles: {
          highlight: new TMap.PolygonStyle({
            color: 'rgba(255, 255, 0, 0.6)'
          })
        },
        geometries: tempList
      })
      this.editor = new TMap.tools.GeometryEditor({
        map,
        overlayList: [{
          overlay: this.polygon,
          id: 'polygon',
        }],
        actionMode: tempList.length > 0 ? TMap.tools.constants.EDITOR_ACTION.INTERACT : TMap.tools.constants.EDITOR_ACTION.DRAW, // ç¼–辑器的工作模式
        activeOverlayId: 'polygon', // æ¿€æ´»å›¾å±‚
        snappable: true, // å¼€å¯å¸é™„
        selectable: true,
      })
      let evtList = ['delete', 'adjust', 'split', 'union']
      evtList.forEach(evtName => {
        this.editor.on(evtName + '_complete', evtResult => {
          // console.log(evtName, evtResult)
        })
      })
      this.editor.on('draw_complete', (geometry) => {
        var id = geometry.id
        if (this.editor.getActiveOverlay().id === 'polygon') {
          var geo = this.polygon.geometries.filter(function (item) {
            return item.id === id
          })
          this.polygons = geo[0].paths
          // console.log('绘制的多边形坐标:', geo[0].paths)
        }
        this.changeMode()
      })
    },
    handleAgain() {
      this.editor.setActionMode(TMap.tools.constants.EDITOR_ACTION.DRAW)
    },
    reset() {
      // this.editor.enable()
      this.polygons = []
      this.polygon.setGeometries([])
      this.polygon.remove('polygon')
      this.editor.setActionMode(TMap.tools.constants.EDITOR_ACTION.DRAW)
    },
    changeMode() {
      this.editor.setActionMode(TMap.tools.constants.EDITOR_ACTION.INTERACT)
    },
    deleteObj() {
      this.editor.delete()
    },
    close() {
      this.reset()
      map.destroy()
      this.isShowModal = false
    },
    confirm() {
      const list = this.editor.getOverlayList()[0].overlay.geometries
      if (list.length == 0) return Message.warning('请先绘制运营区域')
      this.isShowModal = false
      this.reset()
      this.$emit('change', list[0].paths)
    }
  }
}
</script>
<style lang="scss" scoped>
.map_title {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 6px;
}
.map_wrap {
  width: 100%;
  height: calc(100% - 60px);
  position: relative;
  .btns {
    position: absolute;
    left: 50%;
    top: 10px;
    transform: translate(-50%, 0);
    z-index: 99999999999;
  }
}
</style>
admin/src/views/business/goodsorder.vue
@@ -146,6 +146,7 @@
        closeMoney: '',
        startDate: '',
        endDate: '',
        type: 0
      },
      value1: [new Date(), new Date()]
@@ -158,7 +159,7 @@
      'field.id': 'id',
      'field.main': 'id'
    })
    this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    // this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    this.search()
  },
  methods: {
admin/src/views/business/memberRides.vue
@@ -167,8 +167,8 @@
      'field.id': 'id',
      'field.main': 'id'
    })
    this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    this.searchForm.endDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 23:59:59'
    // this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    // this.searchForm.endDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 23:59:59'
    this.search()
  },
  methods: {
admin/src/views/business/miniproSetting.vue
@@ -1,7 +1,7 @@
<template>
  <TableLayout :permissions="['business:baseparam:query']">
    <!-- æœç´¢è¡¨å• -->
    <el-form ref="form" slot="search-form" :model="form" label-width="130px" label-suffix=":">
    <el-form ref="form" slot="search-form" :model="form" label-width="160px" label-suffix=":">
      <el-form-item label="主页主题">
        <el-input v-model="form.projectName" placeholder="请输入主页主题"></el-input>
@@ -10,7 +10,8 @@
        <el-input v-model="form.serverPhone" placeholder="请输入服务电话"></el-input>
      </el-form-item>
      <el-form-item label="押金金额">
        <el-input v-model="form.rentDeposit" @change="changeVal(form.rentDeposit,1)" type="number" placeholder="请输入押金金额"></el-input>
        <el-input v-model="form.rentDeposit" @change="changeVal(form.rentDeposit, 1)" type="number"
          placeholder="请输入押金金额"></el-input>
        <div class="tips">请输入需要缴纳的押金金额(单位:元)</div>
      </el-form-item>
      <el-form-item label="营业时间">
@@ -21,29 +22,55 @@
          v-model="form.businessEndtime"></el-time-picker>
      </el-form-item>
      <el-form-item label="免费骑行时长">
        <el-input v-model="form.freeRentTime" type="number" @change="changeVal(form.freeRentTime,2)" placeholder="请输入免费骑行时长"></el-input>
        <el-input v-model="form.freeRentTime" type="number" @change="changeVal(form.freeRentTime, 2)"
          placeholder="请输入免费骑行时长"></el-input>
        <div class="tips">免费骑行时长,单位:分钟</div>
      </el-form-item>
      <el-form-item label="满载预警(%)" prop="name">
        <div style="display: flex;">
          ä½ŽäºŽ<el-input style="width: 80px;" v-model="form.warnMin" @change="changeVal(form.warnMin,3)" type="number" placeholder="最小值"></el-input>
          æˆ–高于<el-input style="width: 80px;" v-model="form.warnMax" @change="changeVal(form.warnMax,4)" type="number" placeholder="最大值"></el-input>
          ä½ŽäºŽ<el-input style="width: 80px;" v-model="form.warnMin" @change="changeVal(form.warnMin, 3)" type="number"
            placeholder="最小值"></el-input>
          æˆ–高于<el-input style="width: 80px;" v-model="form.warnMax" @change="changeVal(form.warnMax, 4)" type="number"
            placeholder="最大值"></el-input>
          ä¼šæƒ³é’‰é’‰ç¾¤å‘送预警
        </div>
        <div class="tips">当车辆满载超过以上配置时,提示管理员</div>
      </el-form-item>
<!--      <el-form-item label="预警钉钉群地址">-->
<!--        <el-input v-model="form.warnDingdingUrl" placeholder="请输入预警钉钉群地址"></el-input>-->
<!--      </el-form-item>-->
      <el-form-item label="电动车临时停车限制">
        <el-radio v-model="form.pauseBikeType" :label="0">停车点内停车</el-radio>
        <el-radio v-model="form.pauseBikeType" :label="1">运营区内停车</el-radio>
      </el-form-item>
      <el-form-item label="电动车临时自动关锁">
        <el-input v-model="form.pauseAutoCloseTime"
          oninput="value=value.replace(/[^\d.]/g, '').replace(/\.{2,}/g, '.').replace('.', '$#$').replace(/\./g, '').replace('$#$', '.').replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3').replace(/^\./g, '')"
          type="number"></el-input>
        <div class="placeholder9">临停超过配置时长,系统会自动关锁并进行结算,单位:分钟</div>
      </el-form-item>
      <el-form-item label="电动车电压设置">
        <div style="display: flex;">
          ä½Žç”µåŽ‹é¢„è­¦å€¼<el-input style="width: 80px;"
            oninput="value=value.replace(/[^\d.]/g, '').replace(/\.{2,}/g, '.').replace('.', '$#$').replace(/\./g, '').replace('$#$', '.').replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3').replace(/^\./g, '')"
            v-model="form.lowBikeVoltage" type="number"></el-input>
          ä¼ï¼Œæ»¡ç”µç”µåŽ‹ <el-input style="width: 80px;"
            oninput="value=value.replace(/[^\d.]/g, '').replace(/\.{2,}/g, '.').replace('.', '$#$').replace(/\./g, '').replace('$#$', '.').replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3').replace(/^\./g, '')"
            v-model="form.fullBikeVoltage" type="number"></el-input>
        </div>
      </el-form-item>
      <el-form-item label="运营区域设置">
        <el-button type="primary" @click="handleDraw">查看/编辑</el-button>
        <div class="placeholder9">点击按钮可查看/编辑运营区域,用户只能在运营区域内扫码骑行</div>
      </el-form-item>
      <!--      <el-form-item label="预警钉钉群地址">-->
      <!--        <el-input v-model="form.warnDingdingUrl" placeholder="请输入预警钉钉群地址"></el-input>-->
      <!--      </el-form-item>-->
      <el-form-item label="租赁须知主图">
        <div style="display: flex;">
          <UploadAvatarImage
            :file="{ 'imgurlfull': form.leaseNoticeUrl, 'imgurl': leaseNoticeUrl }"
            :uploadData="uploadData" tipsLabel="" @uploadSuccess="uploadOutSuccess" @uploadEnd="isUploading=false"
            @uploadBegin="isUploading=true" />
            <div class="tips">
              <el-button type="text" style="color: red;" @click="form.leaseNoticeUrl = ''">删除</el-button>
            </div>
          <UploadAvatarImage :file="{ 'imgurlfull': form.leaseNoticeUrl, 'imgurl': leaseNoticeUrl }"
            :uploadData="uploadData" tipsLabel="" @uploadSuccess="uploadOutSuccess" @uploadEnd="isUploading = false"
            @uploadBegin="isUploading = true" />
          <div class="tips">
            <el-button type="text" style="color: red;" @click="form.leaseNoticeUrl = ''">删除</el-button>
          </div>
        </div>
      </el-form-item>
      <el-form-item label="租赁流程视频" prop="name">
@@ -51,27 +78,21 @@
          <div style="width: 250px;height: 180px" v-if="form.rentTipsVideo">
            <video controls :src="form.rentTipsVideo" style="width: 100%;height: 100%"></video>
          </div>
<!--          <el-upload class="avatar-uploader" :action="uploadImgUrl" :show-file-list="false" accept=".mp4"-->
<!--            :data="{ folder: 'bike', }" :on-success="handleAvatarSuccess">-->
<!--            <video v-if="form.rentTipsVideo" :src="form.rentTipsVideo" class="avatar"></video>-->
<!--            <i v-else class="el-icon-plus avatar-uploader-icon"></i>-->
<!--          </el-upload>-->
          <!--          <el-upload class="avatar-uploader" :action="uploadImgUrl" :show-file-list="false" accept=".mp4"-->
          <!--            :data="{ folder: 'bike', }" :on-success="handleAvatarSuccess">-->
          <!--            <video v-if="form.rentTipsVideo" :src="form.rentTipsVideo" class="avatar"></video>-->
          <!--            <i v-else class="el-icon-plus avatar-uploader-icon"></i>-->
          <!--          </el-upload>-->
          <div class="tips">
            <div>
              <el-upload
                class="upload-demo"
                ref="upload"
                :action="uploadImgUrl"
                :show-file-list="false"
                accept=".mp4"
                :data="{ folder: 'bike', }"
                :on-success="handleAvatarSuccess">
              <el-upload class="upload-demo" ref="upload" :action="uploadImgUrl" :show-file-list="false" accept=".mp4"
                :data="{ folder: 'bike', }" :on-success="handleAvatarSuccess">
                <el-button slot="trigger" size="small" type="primary">上传视频</el-button>
                <div slot="tip" class="el-upload__tip">只能上传mp4格式,建议不超过50M</div>
              </el-upload>
              <el-button type="text" style="color: red;" @click="form.rentTipsVideo = ''">删除</el-button>
            </div>
<!--            <div>只能上传mp4格式,建议不超过50M</div>-->
            <!--            <div>只能上传mp4格式,建议不超过50M</div>-->
          </div>
        </div>
@@ -97,16 +118,9 @@
          è‡³
          <el-time-picker placeholder="结束时间" value-format="yyyy-MM-dd HH:mm:ss" format='HH:mm'
            v-model="form.stopServeEndtime"></el-time-picker> -->
            <el-date-picker
              v-model="value1"
              type="datetimerange"
              @change="selectDate"
              range-separator="至"
              start-placeholder="开始时间"
              end-placeholder="结束时间"
              format="yyyy-MM-dd HH:mm:ss"
              value-format="yyyy-MM-dd HH:mm:ss"
            ></el-date-picker>
          <el-date-picker v-model="value1" type="datetimerange" @change="selectDate" range-separator="至"
            start-placeholder="开始时间" end-placeholder="结束时间" format="yyyy-MM-dd HH:mm:ss"
            value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker>
          <div class="tips">停止服务开始时间至结束时间</div>
        </el-form-item>
      </template>
@@ -115,6 +129,7 @@
          @click="saveSetting">保存配置项</el-button>
      </el-form-item>
    </el-form>
    <AMap @change="changeMap" ref="MapRef" />
  </TableLayout>
</template>
@@ -123,10 +138,11 @@
import { getMiniProgrammeDTO, updateById } from '@/api/business/minproSetting'
import UploadAvatarImage from '@/components/common/UploadAvatarImage.vue'
import RichEditor from '@/components/common/RichEditor.vue'
import AMap from './components/Map.vue'
export default {
  name: 'BaseParam',
  components: { TableLayout, UploadAvatarImage, RichEditor },
  data () {
  components: { TableLayout, UploadAvatarImage, RichEditor, AMap },
  data() {
    return {
      uploadImgUrl: process.env.VUE_APP_API_PREFIX + '/public/uploadLocal',
      option: {
@@ -158,7 +174,7 @@
      }
    }
  },
  created () {
  created() {
    getMiniProgrammeDTO()
      .then(res => {
        this.form = res
@@ -166,7 +182,11 @@
      })
  },
  methods: {
    changeVal (val, type) {
    changeMap(e) {
      console.log('e', e)
      this.$set(this.form, 'parkLatLngList', JSON.stringify(e))
    },
    changeVal(val, type) {
      if (type === 1) {
        if (this.form.rentDeposit < 0) {
          this.form.rentDeposit = ''
@@ -185,15 +205,24 @@
        }
      }
    },
    handleAvatarSuccess (res, file) {
    handleDraw() {
      this.$refs.MapRef.isShowModal = true
      this.$nextTick(() => {
        if (this.form.parkLatLngList) {
          this.$refs.MapRef.polygons = JSON.parse(this.form.parkLatLngList)
        }
        this.$refs.MapRef.initMap()
      })
    },
    handleAvatarSuccess(res, file) {
      console.log(res.data.url)
      this.form.rentTipsVideo = res.data.url
    },
    uploadOutSuccess (file) {
    uploadOutSuccess(file) {
      this.leaseNoticeUrl = file.imgurl
      this.form.leaseNoticeUrl = file.imgurlfull
    },
    selectDate (v) {
    selectDate(v) {
      this.form.stopServeStarttime = ''
      this.form.stopServeEndtime = ''
      if (v) {
@@ -213,7 +242,7 @@
    //   // }
    //   return isJPG;
    // },
    saveSetting () {
    saveSetting() {
      const reg = /^[0-9][0-9]*|0\.\d*[0-9]\d*$/
      if (!reg.test(this.form.rentDeposit)) {
        return this.$tip.apiFailed({ message: '押金金额必须大于0' })
admin/src/views/business/onlinePayStatistics.vue
@@ -71,7 +71,7 @@
    }
  },
  created () {
    this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    // this.searchForm.startDate = formatDateTime(new Date(), 'yyyy-MM-dd') + ' 00:00:00'
    this.search()
  },
  methods: {
admin/src/views/combo/components/ComboDetail.vue
@@ -8,31 +8,31 @@
          <div class="title_wrap">
            <div>
              <span class="title">{{ detail.name }}</span>
              <span class="status">{{ detail.status === '1' ? '已启用' : '禁用' }}</span>
              <span class="status">{{ detail.status == '0' ? '已启用' : '禁用' }}</span>
            </div>
            <div class="placeholder9 mt10">每日销售限量:{{ detail.circulationDay }}</div>
            <div class="placeholder9 mt10">每日销售限量:{{ detail.saleDayLimit }}</div>
          </div>
        </div>
        <div class="right">
          <div class="item">
            <div class="key">总发放数量</div>
            <div class="value">{{ detail.circulationAll || '-' }}</div>
            <div class="value">{{ detail.saleLimit || '不限额' }}</div>
          </div>
          <div class="item">
            <div class="key">已销售数量</div>
            <div class="value">{{ detail.sellNum }}</div>
            <div class="value">{{ detail.saleNum }}</div>
          </div>
          <div class="item">
            <div class="key">剩余数量</div>
            <div v-if="detail.circulationAll" class="value">{{ detail.surplusDay }}</div>
            <div v-else class="value">{{ detail.surplusDay || '-' }}</div>
            <div v-if="detail.saleLimit" class="value">{{ detail.saleLimit - detail.saleNum }}</div>
            <div v-else class="value">不限额</div>
          </div>
        </div>
      </div>
      <div class="createTime_wrap">
        <span class="item">创建时间:{{ detail.createTime }}</span>
        <span class="item">创建时间:{{ detail.createDate }}</span>
        <span class="item">创建人:{{ detail.creator }}</span>
        <span class="item">最后更新时间:{{ detail.editTime }}</span>
        <span class="item">最后更新时间:{{ detail.editDate }}</span>
        <span class="item">更新人:{{ detail.editor }}</span>
      </div>
    </div>
@@ -44,52 +44,53 @@
    </div>
    <div class="line">
      <div class="key">套餐类型:</div>
      <div v-if="detail.mealsType == '0'" class="value">期限卡</div>
      <div v-if="detail.type == '0'" class="value">期限卡</div>
    </div>
    <div class="line">
      <div class="key">{{ detail.mealsType == '1' || detail.mealsType == '4' ? '每日骑行限制:' : '套餐次数:' }}</div>
      <div v-if="detail.mealsType == '1' || detail.mealsType == '4'" class="value">
        <span>{{ detail.inOutNum || '不限制' }}</span>
      </div>
      <div v-else class="value"><span>{{ detail.nums }}</span></div>
    </div>
    <div class="line">
      <div class="key">使用说明:</div>
      <div class="key">每日骑行限制:</div>
      <div class="value">
        <div>{{ detail.content }}</div>
        <span>{{ detail.limitType == 0 ? '不限制' : detail.limitTime + '分钟' }}</span>
      </div>
    </div>
    <div class="line">
      <div class="key">套餐简介:</div>
      <div class="value">
        <div>{{ detail.desc }}</div>
      </div>
    </div>
    <div class="property-title">适用规则</div>
    <div class="line">
      <div class="key">销售时间段:</div>
      <div class="value">{{ detail.circulationAll || '不限量' }}</div>
      <div class="value">{{ detail.startDate }} è‡³ {{ detail.endDate }}</div>
    </div>
    <div class="line">
      <div class="key">使用时间:</div>
      <div class="value">{{ detail.circulationDay }}</div>
      <div class="value" v-if="detail.useType == 0">{{ detail.useStartDate }} è‡³ {{ detail.useEndDate }}</div>
      <div class="value" v-if="detail.useType == 1">购买后{{ detail.useDays }}天内有效</div>
      <div class="value" v-if="detail.useType == 2">自{{ detail.useStartDate }} åŽ {{ detail.useDays }}天内有效</div>
    </div>
    <div class="line">
      <div class="key">适用范围:</div>
      <div class="value">{{ detail.useVenue }}</div>
      <div class="value">{{ detail.useWorkday == 1 && '工作日' }} {{ detail.useWorkday && detail.useHoliday && '|' }} {{ detail.useHoliday == 1 && '节假日' }}</div>
    </div>
    <div class="line">
      <div class="key">适用项目:</div>
      <div class="value">{{ detail.useProject }}</div>
      <div class="value">{{ detail.isbike == 1 && '自行车' }} {{ detail.isbike && detail.iselecbike && '|' }} {{ detail.iselecbike == 1 && '电动车' }}</div>
    </div>
    <div class="line">
      <div class="key">总限额:</div>
      <div class="value">{{ detail.useClassInfo || '不限制' }}</div>
      <div class="value">{{ detail.saleLimit || '不限制' }}</div>
    </div>
    <div class="line">
      <div class="key">每日限额:</div>
      <div class="value">{{ detail.useTeacherInfo || '不限额' }}</div>
      <div class="value">{{ detail.saleDayLimit || '不限额' }}</div>
    </div>
  </GlobalWindow>
</template>
<script>
// import { comboDetailPost, mealsCancelUse } from '@/api'
// import dayjs from 'dayjs'
import { comboDetailPost } from '@/api/business/combo.js'
import GlobalWindow from '@/components/common/GlobalWindow'
export default {
  name: 'ComboDetail',
@@ -136,24 +137,13 @@
        }
      })
    },
    getDetail(detail) {
      const { activeTab, pagination } = this
      const param = {
        listType: activeTab,
        type: detail.type,
        id: detail.id
      }
      this.tableLoading = true
      comboDetailPost({ pagination, param }).then(res => {
        this.tableLoading = false
        this.dataList = res.record.data || []
        this.totalCount = res.record.count || 0
        this.detail = { ...detail, useProject: res.record.useProject }
        this.$nextTick(() => {
          this.$refs.tableo.doLayout()
        })
    getDetail(id) {
      comboDetailPost(id).then(res => {
        console.log('res', res)
        this.detail = res
      }, () => {
        this.tableLoading = false
      })
    },
    tabClick(val) {
@@ -226,6 +216,7 @@
        border: 1px solid $primaryColor;
        color: $primaryColor;
        margin-left: 10px;
        padding: 0 4px;
      }
    }
@@ -275,10 +266,12 @@
  line-height: 1.5;
  color: #333;
}
.property-title{
.property-title {
  margin-bottom: 16px;
  color: $primaryColor;
}
.device-uploader {
  width: 80px;
  height: 80px;
admin/src/views/combo/components/Edit.vue
@@ -1,35 +1,44 @@
<template>
  <GlobalWindow :title="param.id ? '编辑套餐' : '新建套餐'" :visible.sync="isShowModal" :confirm-working="isLoading"
    width="1000px" @close="close" @confirm="confirm">
    width="900px" @close="close" @confirm="confirm">
    <el-form :model="param" label-width="100px" ref="form" :rules="rules">
      <div class="form_title">基本信息</div>
      <el-form-item label="套餐名称" prop="name">
        <el-input class="w200" v-model="param.name" placeholder="请输入套餐名称" v-trim />
      </el-form-item>
      <el-form-item label="类型">
        <el-select class="w200" :disabled="true" v-model="param.warehouseId">
      <el-form-item label="类型" prop="type">
        <el-select class="w200" :disabled="true" v-model="param.type">
          <el-option label="期限卡" :value="0"></el-option>
          <el-option label="次卡" :value="1"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="每日骑行限制">
        <div>
          <el-radio v-model="param.aa" label="1">不限制</el-radio>
          <el-radio v-model="param.aa" label="2">限制时长</el-radio>
        <div class="df_ac">
          <div class="mr10">
            <el-radio @change="changeType" v-model="param.limitType" :label="0">不限制</el-radio>
            <el-radio @change="changeType" v-model="param.limitType" :label="1">限制时长</el-radio>
          </div>
          <el-input v-if="param.limitType == 1" class="w200" v-model="param.limitTime" v-trim />
          <span v-if="param.limitType == 1" class="ml10">分钟</span>
        </div>
      </el-form-item>
      <el-form-item label="销售价">
        <el-input class="w200" v-model="param.name" placeholder="请输入价格" v-trim />
      <el-form-item label="销售价" prop="price">
        <el-input class="w200"
          oninput="value=value.replace(/[^\d.]/g, '').replace(/\.{2,}/g, '.').replace('.', '$#$').replace(/\./g, '').replace('$#$', '.').replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3').replace(/^\./g, '')"
          v-model="param.price" placeholder="请输入价格" v-trim />
      </el-form-item>
      <el-form-item label="划线价">
        <el-input class="w200" v-model="param.name" placeholder="请输入展示的划线价" v-trim />
        <el-input class="w200" v-model="param.linePrice"
          oninput="value=value.replace(/[^\d.]/g, '').replace(/\.{2,}/g, '.').replace('.', '$#$').replace(/\./g, '').replace('$#$', '.').replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3').replace(/^\./g, '')"
          placeholder="请输入展示的划线价" v-trim />
      </el-form-item>
      <el-form-item label="销售渠道">
        <el-checkbox v-model="param.ada">小程序端</el-checkbox>
      <el-form-item label="销售渠道" prop="channel">
        <el-checkbox v-model="param.channel" disabled :true-label="0" :false-label="1">小程序端</el-checkbox>
      </el-form-item>
      <el-form-item label="套餐图片">
        <div class="df_ac">
          <UploadAvatarImage :file="{ 'imgurlfull': param.fileFullUrl, 'imgurl': param.fileUrl }"
            :uploadData="{ folder: 'ywPatrol/' }" @uploadSuccess="uploadAvatarSuccess" @uploadEnd="isUploading = false"
            :uploadData="{ folder: 'DISCOUNT/' }" @uploadSuccess="uploadAvatarSuccess" @uploadEnd="isUploading = false"
            @uploadBegin="isUploading = true" />
          <div class="img_place">
            <div>上传图片不允许涉及政治敏感与色情;</div>
@@ -39,7 +48,7 @@
        </div>
      </el-form-item>
      <el-form-item label="套餐简介">
        <el-input class="w400" v-model="param.name" placeholder="请输入套餐简介" v-trim />
        <el-input class="w400" v-model="param.descs" placeholder="请输入套餐简介" v-trim />
      </el-form-item>
      <el-form-item label="套餐描述">
        <RichEditor :content="{ content: param.content }" @edit="param.content = $event"></RichEditor>
@@ -48,50 +57,56 @@
        <div>适用规则</div>
        <div class="placeholder9">销售时间、使用时间等设置</div>
      </div>
      <el-form-item label="销售时段">
        <el-date-picker class="w400" v-model="param.value1" value-format="yyyy-MM-dd" type="daterange" range-separator=" ~ " />
      <el-form-item label="销售时段" prop="saleDate">
        <el-date-picker class="w400" v-model="param.saleDate" value-format="yyyy-MM-dd" type="daterange"
          range-separator=" ~ " />
      </el-form-item>
      <div class="df_ac">
        <el-form-item label="使用时间" class="mr16">
          <el-select v-model="param.aaadd" style="width: 140px;">
        <el-form-item prop="useType" label="使用时间" class="mr16">
          <el-select @change="changeUseDate" v-model="param.useType" style="width: 140px;">
            <el-option label="固定日期" :value="0"></el-option>
            <el-option label="购买后生效" :value="1"></el-option>
            <el-option label="指定日期生效" :value="1"></el-option>
            <el-option label="指定日期生效" :value="2"></el-option>
          </el-select>
        </el-form-item>
        <!-- <el-form-item label-width="80px" label="固定日期">
          <el-date-picker v-model="param.value1" value-format="yyyy-MM-dd" type="daterange" range-separator=" ~ " />
        </el-form-item> -->
        <!-- <el-form-item label-width="60px" label="有效期">
          <el-input style="width: 130px;" v-model="param.name" placeholder="请输入有效天数" v-trim />
        </el-form-item> -->
        <el-form-item label-width="0px">
        <el-form-item v-if="param.useType == 0" label-width="80px" label="固定日期">
          <el-date-picker v-model="param.useDate" value-format="yyyy-MM-dd" type="daterange" range-separator=" ~ " />
        </el-form-item>
        <el-form-item v-if="param.useType == 1" label-width="60px" label="有效期">
          <el-input style="width: 130px;" oninput="value=value.replace(/^(0+)|[^\d]+/g, '').slice(0, 8)"
            v-model="param.useDays" placeholder="请输入有效天数" v-trim />
          <span class="ml10">天</span>
        </el-form-item>
        <el-form-item v-if="param.useType == 2" label-width="0px">
          <div class="df_ac">
            <el-date-picker class="w200" v-model="param.dasdas" value-format="yyyy-MM-dd" type="date" />
            <el-date-picker class="w200" v-model="param.useStartDate" value-format="yyyy-MM-dd" type="date" />
            <span class="ml10 mr10">开始生效,有效期</span>
            <el-input style="width: 130px;" v-model="param.name" placeholder="请输入有效天数" v-trim />
            <el-input style="width: 130px;" v-model="param.useDays"
              oninput="value=value.replace(/^(0+)|[^\d]+/g, '').slice(0, 8)" placeholder="请输入有效天数" v-trim />
            <span class="ml10">天</span>
          </div>
        </el-form-item>
      </div>
      <el-form-item label="适用范围">
        <el-checkbox-group v-model="param.checkList">
          <el-checkbox label="工作日"></el-checkbox>
          <el-checkbox label="节假日"></el-checkbox>
        </el-checkbox-group>
      <el-form-item :prop="param.useWorkday == 1 ? 'useWorkday' : 'useHoliday'" label="适用范围">
        <div class="df_ac">
          <el-checkbox v-model="param.useWorkday" :true-label="1" :false-label="0" label="工作日"></el-checkbox>
          <el-checkbox v-model="param.useHoliday" :true-label="1" :false-label="0" label="节假日"></el-checkbox>
        </div>
      </el-form-item>
      <el-form-item label="适用项目">
        <el-checkbox-group v-model="param.checkList">
          <el-checkbox label="自行车"></el-checkbox>
          <el-checkbox label="电动车"></el-checkbox>
        </el-checkbox-group>
      <el-form-item :prop="param.isbike == 1 ? 'isbike' : 'iselecbike'" label="适用项目">
        <div class="df_ac">
          <el-checkbox v-model="param.isbike" :true-label="1" :false-label="0" label="自行车"></el-checkbox>
          <el-checkbox v-model="param.iselecbike" :true-label="1" :false-label="0" label="电动车"></el-checkbox>
        </div>
      </el-form-item>
      <el-form-item label="总限额">
        <el-input class="w400" v-model="param.name" placeholder="请输入总发售数量" v-trim />
        <el-input class="w400" v-model="param.saleLimit" oninput="value=value.replace(/^(0+)|[^\d]+/g, '').slice(0, 12)"
          placeholder="请输入总发售数量" v-trim />
        <div class="placeholder9">请输入总发售数量,销量大于该数量后,不再支持销售;为空表示不限制</div>
      </el-form-item>
      <el-form-item label="日限额">
        <el-input class="w400" v-model="param.name" placeholder="请输入单日发售数量" v-trim />
        <el-input class="w400" v-model="param.saleDayLimit"
          oninput="value=value.replace(/^(0+)|[^\d]+/g, '').slice(0, 12)" placeholder="请输入单日发售数量" v-trim />
        <div class="placeholder9">请输入日发售数量,当日销量大于该数量后,不再支持销售;为空表示不限制</div>
      </el-form-item>
@@ -104,10 +119,9 @@
<script>
import GlobalWindow from '@/components/common/GlobalWindow'
// import { fetchList as getStoreList } from '@/api/ywWarehouse'
// import { ywOutinboundCreate } from '@/api/store/index'
import { comboAddPost, comboEditPost, comboDetailPost } from '@/api/business/combo.js'
import UploadAvatarImage from '@/components/common/UploadAvatarImage'
import { StoreTypeOps, rules } from './config'
import { rulesCombo } from './config'
import { Message } from 'element-ui'
import RichEditor from '@/components/common/RichEditor'
// import dayjs from 'dayjs'
@@ -117,13 +131,25 @@
  data() {
    return {
      // è¡¨å•数据
      param: { checkList: [], content: '' },
      param: {
        type: 0,
        limitType: 0,
        channel: 0,
        useType: 0,
        useWorkday: 1,
        useHoliday: 0,
        iselecbike: 0,
        isbike: 1,
        checkList: [],
        content: ''
      },
      isShowModal: false,
      isShowSel: false,
      isLoading: false,
      isUploading: false,
      // éªŒè¯è§„则
      rules,
      rules: rulesCombo,
      storeList: [],
      list: [],
      StoreTypeOps: [],
@@ -137,47 +163,48 @@
  },
  methods: {
    confirm() {
      const { param } = this
      this.$refs['form'].validate((valid) => {
        if (valid) {
          const { param, list } = this
          if (list.length == 0) return Message.warning('请先选择物料信息')
          let count = 0 // å…¥åº“数量必填
          list.forEach(item => {
            if (!item.stock) count++
          })
          if (count > 0) return Message.warning('请输入正确的入库数量')
          if (param.useDate && param.useDate.length > 0) {
            param.useStartDate = param.useDate[0]
            param.useEndDate = param.useDate[1]
          } else {
            param.useStartDate = param.useType == 2 ? param.useStartDate : null
            param.useEndDate = null
          }
          if (param.saleDate && param.saleDate.length > 0) {
            param.startDate = param.saleDate[0]
            param.endDate = param.saleDate[1]
          } else {
            param.startDate = null
            param.endDate = null
          }
          this.isLoading = true
          ywOutinboundCreate({
            ...param,
            recordList: list
          let fn = param.id ? comboEditPost : comboAddPost
          fn({
            ...param
          }).then(res => {
            this.isLoading = false
            Message.success('提交成功')
            this.$emit('success')
            this.close()
            if (res) {
              Message.success('提交成功')
              this.$emit('success')
              this.close()
            }
          }, () => {
            this.isLoading = false
          })
        }
      })
    },
    initData() {
      this.$set(this.param, 'doneDate', dayjs().format('YYYY-MM-DD'))
      getStoreList({ capacity: 9999, page: 1, model: { status: 0 } }).then(res => {
        this.storeList = res.records || []
      })
    changeType() {
      this.$set(this.param, 'limitTime', null)
    },
    changeSel(val) {
      const list = val
      list.forEach(item => {
        const index = this.list.findIndex(i => i.id == item.id)
        if (index == -1) {
          item.materialId = item.id
          item.multifile = null
          item.createDate = null
          this.list.push(item)
        }
      })
    changeUseDate() {
      this.$set(this.param, 'useDate', null)
      this.$set(this.param, 'useDays', null)
      this.$set(this.param, 'useStartDate', null)
      this.$set(this.param, 'useEndDate', null)
    },
    uploadAvatarSuccess(file) {
      this.$set(this.param, 'fileUrl', file.imgurl)
@@ -191,12 +218,19 @@
      })
    },
    close() {
      console.log('------');
      this.isShowModal = false
      this.$emit('close')
    },
    getDetail(id) {
      getInfoById(id).then(res => {
    getDetail(id, type) {
      comboDetailPost(id).then(res => {
        this.param = res
        this.$set(this.param, 'saleDate', [res.startDate, res.endDate])
        if (res.useStartDate && res.useEndDate) {
          this.$set(this.param, 'useDate', [res.useStartDate, res.useEndDate])
        }
        this.$set(this.param, id, type == 'copy' ? null : this.param.id)
      })
    },
  }
@@ -217,7 +251,8 @@
    margin-left: 12px;
  }
}
.img_place{
.img_place {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
admin/src/views/combo/components/Refund.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,153 @@
<template>
  <GlobalAlertWindow title="套餐卡退款" :visible.sync="visible" :confirm-working="isWorking" width="600px"
    @confirm="confirm">
    <el-descriptions title="" :column="2">
      <el-descriptions-item label="实付金额">{{ form.goodsorderMoney }}元</el-descriptions-item>
      <!-- <el-descriptions-item label="结算金额">{{ form.closeMoney }}元</el-descriptions-item> -->
      <el-descriptions-item label="已退金额">{{ form.hasRefundMoney }}元</el-descriptions-item>
      <el-descriptions-item label="可退金额">{{ form.canBanlanceMoney }}元</el-descriptions-item>
    </el-descriptions>
    <el-form :model="form" ref="form" :rules="rules" label-width="100px" label-suffix=":">
      <el-form-item label="退款类型" prop="backType">
        <el-radio v-model="form.backType" :label="0">退货退款</el-radio>
        <el-radio v-model="form.backType" :label="1">仅退款</el-radio>
        <div class="placeholder9">退款退货会作废订单对应的套餐卡</div>
      </el-form-item>
      <el-form-item label="退款金额" prop="money">
        <div style="display: flex;">
          <el-input style="flex: 1;" oninput="value=value.replace(/[^\d.]/g, '').replace(/\.{2,}/g, '.').replace('.', '$#$').replace(/\./g, '').replace('$#$', '.').replace(/^(\-)*(\d+)\.(\d\d).*$/, '$1$2.$3').replace(/^\./g, '')" v-model="form.money" placeholder="请输入退款金额" v-trim />
        </div>
      </el-form-item>
      <el-form-item label="退款原因" prop="reason">
        <el-input type="textarea" :autosize="{ minRows: 2, maxRows: 4 }" v-model="form.reason" placeholder="请输入退款原因"
          v-trim />
      </el-form-item>
    </el-form>
  </GlobalAlertWindow>
</template>
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalAlertWindow from '@/components/common/GlobalAlertWindow'
import { comboReDetailPost, comboOrderRefundPost } from '@/api/business/combo.js'
export default {
  name: 'OperaSuggestWindow',
  extends: BaseOpera,
  components: { GlobalAlertWindow },
  data() {
    return {
      // è¡¨å•数据
      form: {
        orderId: null,
        canBanlanceMoney: '',
        closeMoney: '',
        goodsorderMoney: '',
        hasRefundMoney: '',
        backType: 0,
        money: '',
        reason: '',
      },
      // éªŒè¯è§„则
      rules: {
        money: [{ required: true, message: '请输入' }],
        reason: [{ required: true, message: '请输入' }],
        backType: [{ required: true, message: '请输入' }]
      },
    }
  },
  created() {
  },
  methods: {
    /**
     * æ‰“开窗口
     * @title çª—口标题
     * @target ç¼–辑的对象
     */
    open(id) {
      this.visible = true
      this.$nextTick(() => {
        comboReDetailPost({ orderId: id }).then(res => {
          if (res) {
            this.form = res
            this.form.backType = 0
            this.form.orderId = id
          }
        })
        this.form.money = null
        this.form.reason = ''
        this.$refs.form.clearValidate()
      })
    },
    selectReason(v) {
      // console.log(item);
      let item = this.reason.find(item => item.name == v)
      this.backInfoRequired = item.required == 1
    },
    confirm() {
      this.$refs.form.validate((valid) => {
        if (!valid) {
          return
        }
        this.isWorking = true
        comboOrderRefundPost(this.form)
          .then((res) => {
            if (res) {
              this.visible = false
              this.$tip.apiSuccess('退款成功')
              this.$emit('success')
            }
          })
          .catch(err => {
            this.$tip.apiFailed(err)
          })
          .finally(() => {
            this.isWorking = false
          })
      })
    }
  },
}
</script>
<style lang="scss" scoped>
@import "@/assets/style/alertstyle.scss";
@import "@/assets/style/variables.scss";
::v-deep .el-input.is-disabled .el-input__inner {
  background-color: #fff !important;
  cursor: pointer;
}
.time-style {
  display: flex;
  flex-wrap: wrap;
  cursor: pointer;
  .time-item {
    margin-right: 8px;
    /* margin-bottom: 8px; */
    border: #111 solid 1px;
    font-size: 14px;
    line-height: 14px;
    padding: 5px;
    border-radius: 5px;
    color: #111;
  }
  .time-item-sel {
    border-color: $primary-color;
    background-color: $primary-color;
    color: #fff;
  }
  .time-item-disable {
    border-color: #999;
    background-color: #999;
    color: #111;
  }
}
</style>
admin/src/views/combo/components/config.js
@@ -1,3 +1,17 @@
export const rulesCombo = {
  name: [{ required: true, message: '请输入' }],
  type: [{ required: true, message: '请输入' }],
  price: [{ required: true, message: '请输入' }],
  channel: [{ required: true, message: '请输入' }],
  saleDate: [{ required: true, message: '请选择' }],
  useType: [{ required: true, message: '请选择' }],
  useWorkday: [{ required: true, message: '请选择' }],
  useHoliday: [{ required: true, message: '请选择' }],
  isbike: [{ required: true, message: '请选择' }],
  iselecbike: [{ required: true, message: '请选择' }],
}
export const pickerOptions = { // ç²¾ç¡®åˆ°ç§’
  disabledDate(time) {
    return time.getTime() < new Date(new Date().toLocaleDateString()).getTime()
admin/src/views/combo/index.vue
@@ -5,19 +5,52 @@
      <el-button type="primary" @click="handleEdit()">新增</el-button>
    </div>
    <el-table v-loading="loading" :data="list" stripe border>
      <el-table-column prop="code" label="套餐名称" align="center" min-width="120" show-overflow-tooltip />
      <el-table-column prop="" label="套餐类型" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="次数" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="有效期" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="ä»·æ ¼" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="总发行数量" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="已售售量" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="销售渠道" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="销售时段" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="状态" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column label="操作" fixed="right" align="center" min-width="80" show-overflow-tooltip>
      <el-table-column prop="name" label="套餐名称" align="center" min-width="120" show-overflow-tooltip>
        <template v-slot="{ row }">
          <span @click="handleDetail(row)" class="primaryColor pointer">查看详情</span>
          <span @click="handleDetail(row)" class="primaryColor pointer">{{ row.name }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="" label="套餐类型" align="center" min-width="100" show-overflow-tooltip>
        <template v-slot="{ row }">
          <span v-if="row.type == 0">期限卡</span>
        </template>
      </el-table-column>
      <!-- <el-table-column prop="" label="次数" align="center" min-width="100" show-overflow-tooltip /> -->
      <el-table-column prop="useDays" label="有效期" align="center" min-width="90" show-overflow-tooltip>
        <template v-slot="{ row }">
          <span>{{ row.useDays }}天</span>
        </template>
      </el-table-column>
      <el-table-column prop="price" label="ä»·æ ¼" align="center" min-width="80" show-overflow-tooltip />
      <el-table-column prop="saleLimit" label="总发行数量" align="center" min-width="100" show-overflow-tooltip>
        <template v-slot="{ row }">
          <span>{{ row.saleLimit ? row.saleLimit : '不限额' }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="saleNum" label="已售售量" align="center" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" label="销售渠道" align="center" min-width="100" show-overflow-tooltip>
        <template v-slot="{ row }">
          <span v-if="row.channel == 0">小程序</span>
        </template>
      </el-table-column>
      <el-table-column prop="" label="销售时段" align="center" min-width="120" show-overflow-tooltip>
        <template v-slot="{ row }">
          <div>起:{{ row.startDate }}</div>
          <div>止:{{ row.endDate }}</div>
        </template>
      </el-table-column>
      <el-table-column prop="" fixed="right" label="状态" align="center" min-width="80" show-overflow-tooltip>
        <template v-slot="{ row }">
          <el-switch @change="changeStatus(row)" v-model="row.status" :active-value="0" :inactive-value="1" />
        </template>
      </el-table-column>
      <el-table-column label="操作" fixed="right" align="center" min-width="120" show-overflow-tooltip>
        <template v-slot="{ row }">
          <div>
            <span v-if="row.status == 0" @click="handleEdit(row, 'copy')" class="primaryColor pointer">复制</span>
            <span v-if="row.status == 1" @click="handleEdit(row)" class="primaryColor pointer mr10">编辑</span>
            <span v-if="row.status == 1" @click="handleDetail(row)" class="red pointer">删除</span>
          </div>
        </template>
      </el-table-column>
    </el-table>
@@ -35,6 +68,8 @@
import TableLayout from '@/layouts/TableLayout'
import Edit from './components/Edit'
import ComboDetail from './components/ComboDetail.vue'
import { comboListPost, comboEditPost } from '@/api/business/combo.js'
import { Message } from 'element-ui'
export default {
  extends: BasePageTemp,
  components: {
@@ -55,27 +90,35 @@
            label: '套餐名称',
          },
          {
            filed: 'type',
            filed: 'bikeOrElec',
            type: 'select',
            label: '适用项目',
            labelCode: 'name',
            valueCode: 'id',
            options: []
            options: [
              { name: '自行车', id: 1 },
              { name: '电动车', id: 2 },
            ]
          },
          {
            filed: 'status',
            type: 'select',
            label: '状态',
            options: []
            labelCode: 'name',
            valueCode: 'id',
            options: [
              { name: '正常', id: 0 },
              { name: '停用', id: 1 },
            ]
          },
        ],
        online: true
      },
      list: [{}]
      list: []
    }
  },
  created() {
    // this.getList()
    this.getList()
    // this.initData()
  },
  methods: {
@@ -86,17 +129,27 @@
        }
      })
    },
    handleEdit() {
    changeStatus(row) {
      comboEditPost({ ...row }).then(res => {
        if (res.code == 200) {
          return Message.success('更新成功')
        }
      })
    },
    handleEdit(row, type = 'edit') {
      this.isShowEdit = true
      this.$nextTick(() => {
        this.$refs.EditRef.isShowModal = true
        if (row && row.id) {
          this.$refs.EditRef.getDetail(row.id, type)
        }
      })
    },
    handleDetail(row) {
      this.isShowDetail = true
      this.$nextTick(() => {
        this.$refs.DetailRef.isShowModal = true
        // this.$refs.DetailRef.getDetail(row.id)
        this.$refs.DetailRef.getDetail(row.id)
      })
    },
    handleEx() {
@@ -129,20 +182,15 @@
      const { pagination, filters } = this
      this.loading = true
      if (page) { pagination.page = page }
      ywOutinboundPage({
      comboListPost({
        model: {
          ...filters,
          inOut: 0
          ...filters
        },
        // sorts: [{ direction: 'DESC', property: 'param1' }],
        capacity: pagination.pageSize,
        page: page,
      }).then(res => {
        this.loading = false
        this.list = res.records || []
        this.list.forEach(item => {
          item.typeName = this.StoreTypeOps[item.type].name
        })
        this.pagination.total = res.total || 0
      }, () => {
        this.loading = false
admin/src/views/combo/order.vue
@@ -6,21 +6,33 @@
      <el-button type="primary" @click="handleEx()">导出</el-button>
    </div>
    <el-table v-loading="loading" :data="list" stripe border>
      <el-table-column prop="code" align="center" label="订单编号" min-width="140" show-overflow-tooltip>
      <el-table-column prop="code" align="center" label="订单编号" min-width="180" show-overflow-tooltip>
        <template scope="{row}">
          <span class="primaryColor pointer">{{ row.orderId }}</span>
          <span @click="handleDetail(row.id)" class="primaryColor pointer">{{ row.code }}</span>
        </template>
      </el-table-column>
      <el-table-column prop="" align="center" label="套餐卡" min-width="120" show-overflow-tooltip />
      <el-table-column prop="" align="center" label="合计(元)" min-width="80" show-overflow-tooltip />
      <el-table-column prop="" align="center" label="实付(元)" min-width="80" show-overflow-tooltip />
      <el-table-column prop="" align="center" label="已退金额(元)" min-width="100" show-overflow-tooltip />
      <el-table-column prop="" align="center" label="用户信息" min-width="120" show-overflow-tooltip />
      <el-table-column prop="" align="center" label="订单状态" min-width="110" show-overflow-tooltip />
      <el-table-column prop="discountName" align="center" label="套餐卡" min-width="120" show-overflow-tooltip />
      <el-table-column prop="money" align="center" label="合计(元)" min-width="80" show-overflow-tooltip />
      <el-table-column prop="money" align="center" label="实付(元)" min-width="80" show-overflow-tooltip />
      <el-table-column prop="refundMoney" align="center" label="已退金额(元)" min-width="100" show-overflow-tooltip />
      <el-table-column prop="memberId" align="center" label="用户信息" min-width="140" show-overflow-tooltip />
      <el-table-column prop="payWay" align="center" label="支付方式" min-width="80" show-overflow-tooltip>
        <template v-slot="{ row }">
          <span v-if="row.payWay == 0">微信支付</span>
          <span v-if="row.payWay == 1">支付宝</span>
        </template>
      </el-table-column>
      <el-table-column prop="payWay" align="center" label="订单来源" min-width="80" show-overflow-tooltip>
        <template v-slot="{ row }">
          <span>小程序端</span>
        </template>
      </el-table-column>
      <el-table-column prop="refundUserName" align="center" label="操作人" min-width="80" show-overflow-tooltip />
      <el-table-column prop="createDate" align="center" label="创建时间" min-width="140" show-overflow-tooltip />
      <el-table-column label="操作" fixed="right" align="center" min-width="80" show-overflow-tooltip>
        <template v-slot="{ row }">
          <span @click="handleDetail(row.id)" v-permissions="['business:ywoutinboundrecord:query']"
            class="primaryColor pointer">查看详情</span>
          <span @click="handleDetail(row.id)" class="primaryColor pointer">查看详情</span>
          <span v-if="row.status == 1 && (row.refundMoney < row.money)" @click="handRefund(row.id)" class="primaryColor pointer ml10">退款</span>
        </template>
      </el-table-column>
    </el-table>
@@ -28,7 +40,8 @@
      <Pagination @size-change="handleSizeChange" @current-change="getList" :pagination="pagination" />
    </div>
    <!--  -->
    <OrderDetail v-if="isShowDetail" ref="OrderDetailRef" />
    <OrderDetail ref="OrderDetailRef" />
    <Refund ref="RefundRef" @success="getList" />
  </div>
</template>
@@ -36,20 +49,26 @@
import BasePageTemp from '@/components/base/BasePageTemp'
import Breadcrumb from '@/layouts/Breadcrumb'
import OrderDetail from './components/OrderDetail.vue'
import Refund from './components/Refund'
import {
  comboOrderPost,
  comboListOrderEx,
  comboOrderDetailPost
} from '@/api/business/combo.js'
export default {
  extends: BasePageTemp,
  components: {
    Breadcrumb,
    OrderDetail
    OrderDetail,
    Refund
  },
  data() {
    return {
      loading: false,
      isShowDetail: false,
      queryFormConfig: {
        formItems: [
          {
            filed: 'orderId',
            filed: 'id',
            type: 'input',
            label: '订单编号',
          },
@@ -69,7 +88,10 @@
            filed: 'status',
            type: 'select',
            label: '订单状态',
            options: []
            options: [
              { label: '未支付', value: 0 },
              { label: '已支付', value: 1 },
            ]
          },
          {
            filed: 'time',
@@ -87,12 +109,15 @@
    }
  },
  created() {
    // this.getList()
    this.getList()
    // this.initData()
  },
  methods: {
    handRefund(id) {
      this.$refs.RefundRef.open(id)
    },
    handleDetail(id) {
      getDetail(id)
      comboOrderDetailPost({id})
        .then(res => {
          this.$refs.OrderDetailRef.open('订单详情', res)
        })
@@ -104,8 +129,8 @@
      this.$dialog.exportConfirm('确认导出吗?')
        .then(() => {
          this.loading = true
          ywOutinboundEx({
            page: this.pagination.page,
          comboListOrderEx({
            page: 1,
            capacity: 1000000,
            model: this.filters
          })
@@ -130,20 +155,16 @@
      const { pagination, filters } = this
      this.loading = true
      if (page) { pagination.page = page }
      ywOutinboundPage({
      comboOrderPost({
        model: {
          ...filters,
          inOut: 0
          type: 1
        },
        // sorts: [{ direction: 'DESC', property: 'param1' }],
        capacity: pagination.pageSize,
        page: page,
        page: pagination.page,
      }).then(res => {
        this.loading = false
        this.list = res.records || []
        this.list.forEach(item => {
          item.typeName = this.StoreTypeOps[item.type].name
        })
        this.pagination.total = res.total || 0
      }, () => {
        this.loading = false
admin/src/views/operation/components/AMap.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,144 @@
<template>
  <GlobalWindow title="绘制运营区域" :visible.sync="isShowModal" :confirm-working="isWorking" @close="close"
    @confirm="confirm" width="1000px">
    <div class="map_title">绘制:鼠标左键点击及移动即可绘制图形,绘制过程中按下esc键可中断该过程</div>
    <div class="map_title">鼠标左键双击即可结束当前多边形绘制,多边形会自动闭合;</div>
    <div class="map_wrap">
      <div class="btns">
        <el-button plain type="primary" @click="reset">{{ polygons.length > 0 ? '重新绘制' : '开始绘制' }}</el-button>
        <!-- <el-button plain type="primary" @click="changeMode">结束绘制</el-button> -->
        <!-- <el-button plain @click="reset">重新绘制</el-button> -->
        <!-- <el-button plain  type="danger" @click="deleteObj">删除</el-button> -->
      </div>
      <div id="container" style="width: 100%; height: 100%"></div>
    </div>
  </GlobalWindow>
</template>
<script>
import GlobalWindow from '@/components/common/GlobalWindow'
import { Message } from 'element-ui'
var map // åœ°å›¾
// var editor // ç¼–辑器
export default {
  components: {
    GlobalWindow
  },
  data() {
    return {
      isShowModal: false,
      isWorking: false,
      row: {},
      map: null,
      editor: null,
      polygon: null,
      polygons: [],            // å­˜å‚¨æ‰€æœ‰ç»˜åˆ¶çš„多边形
      selectedPolygon: null,
    }
  },
  methods: {
    initMap() {
      map = new TMap.Map("container", {
        zoom: 15, // è®¾ç½®åœ°å›¾ç¼©æ”¾çº§åˆ«
        center: new TMap.LatLng(31.722763, 117.385480) // è®¾ç½®åœ°å›¾ä¸­å¿ƒç‚¹åæ ‡
      })
      var tempList = []
      if (this.polygons.length > 0) {
        tempList = [{ paths: this.polygons.map(item => new TMap.LatLng(item.lat, item.lng)) }]
      }
      this.polygon = new TMap.MultiPolygon({
        map: map,
        id: 'polygon',
        selectedStyleId: 'highlight',
        styles: {
          highlight: new TMap.PolygonStyle({
            color: 'rgba(255, 255, 0, 0.6)'
          })
        },
        geometries: tempList
      })
      this.editor = new TMap.tools.GeometryEditor({
        map,
        overlayList: [{
          overlay: this.polygon,
          id: 'polygon',
        }],
        actionMode: tempList.length > 0 ? TMap.tools.constants.EDITOR_ACTION.INTERACT : TMap.tools.constants.EDITOR_ACTION.DRAW, // ç¼–辑器的工作模式
        activeOverlayId: 'polygon', // æ¿€æ´»å›¾å±‚
        snappable: true, // å¼€å¯å¸é™„
        selectable: true,
      })
      let evtList = ['delete', 'adjust', 'split', 'union']
      evtList.forEach(evtName => {
        this.editor.on(evtName + '_complete', evtResult => {
          // console.log(evtName, evtResult)
        })
      })
      this.editor.on('draw_complete', (geometry) => {
        var id = geometry.id
        if (this.editor.getActiveOverlay().id === 'polygon') {
          var geo = this.polygon.geometries.filter(function (item) {
            return item.id === id
          })
          this.polygons = geo[0].paths
          // console.log('绘制的多边形坐标:', geo[0].paths)
        }
        this.changeMode()
      })
    },
    handleAgain() {
      this.editor.setActionMode(TMap.tools.constants.EDITOR_ACTION.DRAW)
    },
    reset() {
      // this.editor.enable()
      this.polygons = []
      this.polygon.setGeometries([])
      this.polygon.remove('polygon')
      this.editor.setActionMode(TMap.tools.constants.EDITOR_ACTION.DRAW)
    },
    changeMode() {
      this.editor.setActionMode(TMap.tools.constants.EDITOR_ACTION.INTERACT)
    },
    deleteObj() {
      this.editor.delete()
    },
    close() {
      this.reset()
      map.destroy()
      this.isShowModal = false
    },
    confirm() {
      const list = this.editor.getOverlayList()[0].overlay.geometries
      if (list.length == 0) return Message.warning('请先绘制运营区域')
      this.isShowModal = false
      this.reset()
      this.$emit('change', list[0].paths, this.row)
    }
  }
}
</script>
<style lang="scss" scoped>
.map_title {
  font-size: 14px;
  font-weight: 500;
  margin-bottom: 6px;
}
.map_wrap {
  width: 100%;
  height: calc(100% - 60px);
  position: relative;
  .btns {
    position: absolute;
    left: 50%;
    top: 10px;
    transform: translate(-50%, 0);
    z-index: 99999999999;
  }
}
</style>
admin/src/views/operation/components/QRcode.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,122 @@
<template>
    <GlobalWindow
        title="查看图片"
        :visible.sync="visible"
        :confirm-working="isWorking"
        confirmText="打印二维码"
    >
        <div class="box" id="print-content">
            <div class="box_item" v-for="(item, index) in list" :key="index">
<!--                <div class="box_item_title">微信</div>-->
                <div class="box_item_qr">
                    <img :src="item.imgurl" />
<!--                    <span>{{item.siteId}}/{{item.code}}</span>-->
                </div>
<!--                <div class="box_item_footer">扫码取车</div>-->
            </div>
        </div>
        <template v-slot:footer>
<!--            <el-button type="primary" v-print="'#print-content'">打印</el-button>-->
            <el-button type="primary" :loading="isWorking.export" @click="exportImages">打包下载</el-button>
            <el-button @click="close">返回</el-button>
        </template>
    </GlobalWindow>
</template>
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalWindow from '@/components/common/GlobalWindow'
import { downloadImgs } from '@/api/business/bikes.js'
export default {
  name: 'QRcode',
  extends: BaseOpera,
  components: { GlobalWindow },
  data () {
    return {
      siteId:'',
      isLoading : true,
      list: []
    }
  },
  created () {
    this.config({
      api: '/business/sites',
      'field.id': 'id'
    })
  },
  methods: {
    open (title, id) {
      this.visible = true
    },
    close () {
      this.visible = false
    },
    exportImages () {
      downloadImgs( this.siteId)
          .then(response => {
            this.download(response)
          })
          .catch(e => {
            this.$tip.apiFailed(e)
          })
          .finally(() => {
            this.isWorking.export = false
          })
    }
  }
}
</script>
<style lang="scss" scoped>
    .box {
        width: 100%;
        display: flex;
        align-items: center;
        /*justify-content: space-between;*/
        flex-wrap: wrap;
        .box_item {
            width: 152px;
            height: auto;
            display: flex;
            flex-direction: column;
            justify-content: space-between;
            align-items: center;
            background: #FFFFFF;
            border: 1px solid #BBBBBB;
            padding: 3px 0 4px 0;
            box-sizing: border-box;
            margin-top: 50px;
            margin-left: 35px;
            .box_item_title {
                font-size: 21px;
                font-weight: 400;
                color: #222222;
            }
            .box_item_qr {
                width: 139px;
                height: 154px;
                padding: 6px 7px;
                box-sizing: border-box;
                background: #282828;
                display: flex;
                flex-direction: column;
                align-items: center;
                justify-content: space-between;
                img {
                    width: 126px;
                    height: 146px;
                }
                span {
                    font-size: 13px;
                    font-weight: 400;
                    color: #FFFFFF;
                }
            }
            .box_item_footer {
                font-size: 23px;
                font-weight: bold;
                color: #222222;
            }
        }
    }
</style>
admin/src/views/operation/components/SitesWindow.vue
@@ -13,12 +13,12 @@
      <el-form-item label="站点名称" prop="name">
        <el-input v-model="form.name" placeholder="请输入站点名称" v-trim/>
      </el-form-item>
      <el-form-item label="是否异常上报" prop="needNotice">
      <!-- <el-form-item label="是否异常上报" prop="needNotice">
        <el-radio-group v-model="form.needNotice">
          <el-radio :label="0">上报</el-radio>
          <el-radio :label="1">不上报</el-radio>
        </el-radio-group>
      </el-form-item>
      </el-form-item> -->
      <!-- <el-form-item label="站点ip" prop="ip">
        <el-input v-model="form.ip" placeholder="请输入站点ip地址" v-trim/>
      </el-form-item>
@@ -49,7 +49,8 @@
        code: '',
        needNotice:0,
        ip: '',
        port: ''
        port: '',
        type: 1
        // createDate: '',
        // creator: '',
admin/src/views/operation/components/TramEdit.vue
@@ -7,17 +7,16 @@
    width="600px"
  >
    <el-form :model="form" ref="form" :rules="rules" label-width="100px">
      <el-form-item label="车辆类型" prop="code">
        <el-select  v-model="form.named" placeholder="请选择">
          <el-option label="3人电动车" :value="0"></el-option>
          <el-option label="4人电动车" :value="1"></el-option>
      <el-form-item label="车辆类型" prop="paramId">
        <el-select  v-model="form.paramId" placeholder="请选择">
          <el-option v-for="item in types" :label="item.name" :value="item.id"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="车辆编号" prop="code">
        <el-input v-model="form.name" placeholder="请输入车辆编号" v-trim/>
        <el-input v-model="form.code" placeholder="请输入车辆编号" v-trim/>
      </el-form-item>
      <el-form-item label="控制器SN" prop="needNotice">
        <el-input v-model="form.name" placeholder="请输入控制器SN" v-trim/>
      <el-form-item label="控制器SN" prop="deviceSn">
        <el-input v-model="form.deviceSn" placeholder="请输入控制器SN" v-trim/>
      </el-form-item>
    </el-form>
  </GlobalWindow>
@@ -26,6 +25,7 @@
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalWindow from '@/components/common/GlobalWindow'
import { baseTranListPost } from '@/api/business/bikes.js'
export default {
  name: 'OperaSitesWindow',
  extends: BaseOpera,
@@ -34,36 +34,38 @@
    return {
      // è¡¨å•数据
      form: {
        id: null,
        name: '',
        paramId: '',
        code: '',
        needNotice:0,
        ip: '',
        port: ''
        deviceSn: '',
      },
      types: [],
      // éªŒè¯è§„则
      rules: {
        code: [
          { required: true, message: '请输入站点编号', tigger: 'blur' },
          { required: true, message: '请输入', tigger: 'blur' },
        ],
        name: [
          { required: true, message: '请输入站点名称', tigger: 'blur' },
        paramId: [
          { required: true, message: '请选择', tigger: 'blur' },
        ],
        ip: [
          { required: true, message: '请输入站点ip地址', tigger: 'blur' },
        ],
        port: [
          { required: true, message: '请输入站点端口号', tigger: 'blur' },
        deviceSn: [
          { required: true, message: '请输入', tigger: 'blur' },
        ],
      }
    }
  },
  created () {
    this.initOp()
    this.config({
      api: '/business/sites',
      api: '/business/bikes',
      'field.id': 'id'
    })
  },
  methods: {
    initOp(){
      baseTranListPost({type: 4}).then(res => {
        this.types = res
      })
    }
  }
}
</script>
admin/src/views/operation/site.vue
@@ -25,7 +25,7 @@
          min-width="100px" align="center"></el-table-column>
        <el-table-column prop="name" label="站点名称" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="createTime" label="创建时间" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="createDate" label="创建时间" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column v-if="containPermissions(['business:sites:update', 'business:sites:delete'])" label="操作"
          min-width="120" align="center" fixed="right">
          <template slot-scope="{row}">
@@ -43,8 +43,7 @@
    <!-- æ–°å»º/修改 -->
    <OperaSitesWindow ref="operaSitesWindow" @success="handlePageChange" />
    <!--  æŸ¥çœ‹äºŒç»´ç   -->
    <AMap ref="MapRef" />
    <div id="aaa"></div>
    <AMap @change="changeMap" ref="MapRef" />
  </TableLayout>
</template>
@@ -53,7 +52,9 @@
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaSitesWindow from './components/SitesWindow'
import AMap from './components/Map.vue'
import AMap from './components/AMap.vue'
import { Message } from 'element-ui'
import { updateById } from '@/api/business/sites.js'
export default {
  name: 'Sites',
  extends: BaseTable,
@@ -74,7 +75,8 @@
        status: '',
        lockNum: '',
        ip: '',
        clientVersion: ''
        clientVersion: '',
        type: 1
      },
      isShowMap: false
    }
@@ -89,10 +91,23 @@
    this.search()
  },
  methods: {
    handleDraw() {
    changeMap(e, row) {
      console.log(e);
      updateById({ ...row, electronicFence: JSON.stringify(e) }).then(res => {
        if (res) {
          Message.success('设置成功')
          this.search()
        }
      })
    },
    handleDraw(row) {
      this.$refs.MapRef.isShowModal = true
      this.$nextTick(() => {
        // this.$refs.MapRef.isShowModal = true
        this.$refs.MapRef.row = row
        if (row.electronicFence) {
          this.$refs.MapRef.polygons = JSON.parse(row.electronicFence)
        }
        this.$refs.MapRef.initMap()
      })
    }
admin/src/views/operation/tram.vue
@@ -15,54 +15,65 @@
        </el-select>
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button type="primary" @click="search(1)">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </section>
    </el-form>
    <!-- è¡¨æ ¼å’Œåˆ†é¡µ -->
    <template v-slot:table-wrap>
      <ul class="toolbar" v-permissions="['business:sites:create']">
        <li><el-button type="primary" @click="$refs.TramEditRef.open('新增车辆')" icon="el-icon-plus" v-permissions="['business:sites:create']">新建</el-button></li>
        <li><el-button type="primary" @click="$refs.TramEditRef.open('新增车辆')" icon="el-icon-plus"
            v-permissions="['business:sites:create']">新建</el-button></li>
        <li><el-button @click="handleDelAll">批量删除</el-button></li>
        <li v-permissions="['system:user:create']">
          <ImportButton text="导入" template-name="电车模板.xlsx" template-path="/template/tram_import_tem.xlsx"
            action="/business/bikes/importExcel" @success="search" />
        </li>
        <li><el-button :loading="downLoading" @click="handleDownAll"
            v-permissions="['business:sites:create']">批量下载二维码</el-button></li>
      </ul>
      <el-table
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
        border
      >
        <el-table-column prop="code" label="车辆编码"  :sort-method="(a,b)=>{ return   a.code-b.code}"  sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="name" label="车辆类型" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="name" label="控制器SN" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="name" label="当前电压值" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="name" label="电量情况" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="name" label="站点编号" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="name" label="站点名称" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column prop="createTime" label="最后通讯时间" sortable  min-width="100px" align="center"></el-table-column>
        <el-table-column
          v-if="containPermissions(['business:sites:update', 'business:sites:delete'])"
          label="操作"
          min-width="160"
          align="center"
          fixed="right"
        >
      <el-table @selection-change="handleSelectionChange" v-loading="isWorking.search" :data="tableData.list" stripe
        border>
        <el-table-column type="selection" width="55" align="center"></el-table-column>
        <el-table-column prop="code" label="车辆编码" min-width="100px" align="center"></el-table-column>
        <el-table-column prop="paramName" label="车辆类型" min-width="100px" align="center"></el-table-column>
        <el-table-column prop="deviceSn" label="控制器SN" min-width="100px" align="center"></el-table-column>
        <el-table-column prop="voltage" label="当前电压值" sortable min-width="100px" align="center"></el-table-column>
        <el-table-column label="电量情况" width="80px" align="center">
          <template v-slot="{ row }">
            <span :class="{ red: row.lowVoltage == 1 }">{{ row.lowVoltage == 0 ? '正常' : '低电量' }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="siteId" label="站点编号" min-width="100px" align="center"></el-table-column>
        <el-table-column prop="siteName" label="站点名称" min-width="100px" align="center"></el-table-column>
        <el-table-column prop="heartDate" label="最后通讯时间" min-width="100px" align="center"></el-table-column>
        <el-table-column v-if="containPermissions(['business:sites:update', 'business:sites:delete'])" label="操作"
          min-width="160" align="center" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="$refs.QRcode.open('查看二维码', row.code)" icon="el-icon-picture-outline-round">查看二维码</el-button>
            <el-button type="text" @click="$refs.TramEditRef.open('编辑车辆', row)" icon="el-icon-edit" v-permissions="['business:sites:update']">编辑</el-button>
            <el-button type="text" @click="deleteById(row)" icon="el-icon-delete" v-permissions="['business:sites:delete']">删除</el-button>
            <el-button type="text" @click="handlePri(row)" icon="el-icon-picture-outline-round">查看二维码</el-button>
            <el-button type="text" @click="$refs.TramEditRef.open('编辑车辆', row)" icon="el-icon-edit"
              v-permissions="['business:sites:update']">编辑</el-button>
            <el-button type="text" @click="deleteById(row)" icon="el-icon-delete"
              v-permissions="['business:sites:delete']">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      >
      <pagination @size-change="handleSizeChange" @current-change="handlePageChange" :pagination="tableData.pagination">
      </pagination>
    </template>
    <!-- æ–°å»º/修改 -->
    <TramEdit ref="TramEditRef" @success="handlePageChange"/>
    <!--  æŸ¥çœ‹äºŒç»´ç   -->
    <QRcode ref="QRcode" />
    <TramEdit ref="TramEditRef" @success="search" />
    <el-dialog title="查看二维码" :visible.sync="isShowPri" width="600px">
      <div style="display: flex;align-items: center;justify-content: center;">
        <el-image style="width: 500px; height: 500px" :src="imgurl" :preview-src-list="[imgurl]"></el-image>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="isShowPri = false">取 æ¶ˆ</el-button>
        <el-button type="primary" @click="isShowPri = false">ç¡® å®š</el-button>
      </span>
    </el-dialog>
    <QRcode ref="QRcodeRef" />
    <!-- <ImportWindow ref="ImportWindowRef" templateName="电车信息模板" /> -->
  </TableLayout>
</template>
@@ -71,12 +82,16 @@
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import TramEdit from './components/TramEdit.vue'
import QRcode from '@/components/business/QRcode'
import QRcode from './components/QRcode'
import ImportButton from '@/components/common/ImportButton'
import { tranListPost } from '@/api/business/bikes.js'
import { qrcodeBike, qrcodeBikeList, deleteBatchById } from '@/api/business/bikes.js'
import { Message } from 'element-ui'
export default {
  name: 'Sites',
  name: 'bikes',
  extends: BaseTable,
  components: { TableLayout, Pagination, TramEdit, QRcode },
  data () {
  components: { TableLayout, Pagination, TramEdit, QRcode, ImportButton },
  data() {
    return {
      // æœç´¢
      searchForm: {
@@ -92,18 +107,122 @@
        status: '',
        lockNum: '',
        ip: '',
        type: 1,
        clientVersion: ''
      }
      },
      imgurl: '',
      isShowPri: false,
      downLoading: false,
      selList: []
    }
  },
  created () {
  created() {
    this.config({
      module: '站点信息表',
      api: '/business/sites',
      api: '/business/bikes',
      'field.id': 'id',
      'field.main': 'id'
    })
    this.search()
  },
  methods: {
    handleSelectionChange(val) {
      this.selList = val
      console.log(val)
    },
    handleDelAll() {
      const { selList } = this
      if (selList.length == 0) return Message.warning('请先选择要删除的车辆')
      this.$confirm('您确认要删除选中的车辆吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning',
      }).then(() => {
        deleteBatchById({ ids: selList.map(i => i.id).join() }).then(res => {
          if (res) {
            this.search(1)
            Message.success('删除成功')
          }
        })
      })
    },
    handlePri(row) {
      this.imgurl = null
      qrcodeBike({ fouce: 0, bikeId: row.id }).then(res => {
        if (res && res.imgurl) {
          this.imgurl = res.imgurl
          this.isShowPri = true
        }
      })
    },
    handleDownAll() {
      this.downLoading = true
      qrcodeBikeList({ fouce: 0 }).then(res => {
        this.downLoading = false
        this.$refs.QRcodeRef.list = res
        this.$refs.QRcodeRef.visible = true
      }, () => {
        this.downLoading = false
      })
    },
    search(pageIndex) {
      this.tableData.pagination.pageIndex = pageIndex || this.tableData.pagination.pageIndex
      this.isWorking.search = true
      tranListPost({
        page: this.tableData.pagination.pageIndex,
        capacity: this.tableData.pagination.pageSize,
        model: this.searchForm,
        sorts: this.tableData.sorts
      })
        .then(data => {
          this.tableData.list = data.records
          this.tableData.pagination.total = data.total
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
        .finally(() => {
          this.isWorking.search = false
        })
    },
    handleSizeChange(pageSize) {
      this.tableData.pagination.pageSize = pageSize
      this.search()
    },
    // æŽ’序
    handleSortChange(sortData) {
      this.tableData.sorts = []
      if (sortData.order != null) {
        this.tableData.sorts.push({
          property: sortData.column.sortBy,
          direction: sortData.order === 'descending' ? 'DESC' : 'ASC'
        })
      }
      this.handlePageChange()
    },
    // é¡µç å˜æ›´å¤„理
    handlePageChange(pageIndex) {
      this.tableData.pagination.pageIndex = pageIndex || this.tableData.pagination.pageIndex
      this.isWorking.search = true
      tranListPost({
        page: this.tableData.pagination.pageIndex,
        capacity: this.tableData.pagination.pageSize,
        model: this.searchForm,
        sorts: this.tableData.sorts
      })
        .then(data => {
          this.tableData.list = data.records
          this.tableData.pagination.total = data.total
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
        .finally(() => {
          this.isWorking.search = false
        })
    },
  }
}
</script>