MrShi
4 天以前 be3ec4c1f11a5e090408fcd6f650557651fcf007
页面
已添加24个文件
已修改4个文件
3428 ■■■■■ 文件已修改
admin/src/api/business/banner.js 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/driver.js 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/goodsCategory.js 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/memberManage.js 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/revenue.js 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/shopInfo.js 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/shopWithdraw.js 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/sysParams.js 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaBannerEditWindow.vue 135 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaGoodsCategoryEditWindow.vue 204 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaMemberDetail.vue 185 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaShopApprovalWindow.vue 450 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaShopEditWindow.vue 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaShopInfoSeeWindow.vue 416 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaShopInfoWindow.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/components/business/OperaWithdrawDetailWindow.vue 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/baggageType.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/bannerList.vue 138 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/driverList.vue 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/goodsCategoryList.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/itemLevel.vue 96 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/memberList.vue 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/memberManage.vue 149 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/shopQualificationList.vue 155 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/shopWithdrawList.vue 156 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/storeList.vue 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/sysParams.vue 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/views/business/vehicleType.vue 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
admin/src/api/business/banner.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
import request from '../../utils/request'
export function fetchList (data) {
  return request.post('/business/banner/page', data, {
    trim: true
  })
}
export function create (data) {
  return request.post('/business/banner/create', data)
}
export function updateStatus (data) {
  return request.post('/business/banner/updateStatus', data)
}
export function updateById (data) {
  return request.post('/business/banner/updateById', data)
}
export function getById (id) {
  return request.get(`/business/banner/${id}`)
}
export function deleteById (id) {
  return request.get(`/business/banner/delete/${id}`)
}
export function deleteByIdInBatch (ids) {
  return request.get('/business/banner/delete/batch', {
    params: {
      ids
    }
  })
}
// å¯¼å‡ºExcel
export function exportExcel (data) {
  return request.post('/business/banner/exportExcel', data, {
    download: true,
    trim: true
  })
}
admin/src/api/business/driver.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
import request from '../../utils/request'
export function fetchList (data) {
  return request.post('/business/driver/page', data, {
    trim: true
  })
}
export function getById (id) {
  return request.get(`/business/driver/${id}`)
}
export function updateStatus (data) {
  return request.post('/business/driver/updateStatus', data, {
    trim: true
  })
}
export function exportExcel (data) {
  return request.post('/business/driver/exportExcel', data, {
    download: true,
    trim: true
  })
}
admin/src/api/business/goodsCategory.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
import request from '../../utils/request'
export function fetchList (data) {
  return request.post('/business/category/page', data, {
    trim: true
  })
}
export function create (data) {
  return request.post('/business/category/create', data, {
    trim: true
  })
}
export function updateStatus (data) {
  return request.post('/business/category/updateStatus', data, {
    trim: true
  })
}
export function updateById (data) {
  return request.post('/business/category/updateById', data, {
    trim: true
  })
}
export function getById (id) {
  return request.get(`/business/category/${id}`)
}
export function deleteById (id) {
  return request.get(`/business/category/delete/${id}`)
}
admin/src/api/business/memberManage.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
import request from '../../utils/request'
export function fetchList (data) {
  return request.post('/business/member/list/page', data, {
    trim: true
  })
}
export function getById (id) {
  return request.get(`/business/member/detail/${id}`)
}
export function exportExcel (data) {
  return request.post('/business/member/list/exportExcel', data, {
    download: true,
    trim: true
  })
}
admin/src/api/business/revenue.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
import request from '../../utils/request'
export function fetchList (data) {
  return request.post('/business/revenue/page', data, {
    trim: true
  })
}
export function create (data) {
  return request.post('/business/revenue/create', data)
}
export function updateStatus (data) {
  return request.post('/business/revenue/updateStatus', data)
}
export function updateById (data) {
  return request.post('/business/revenue/updateShop', data)
}
export function deleteById (id) {
  return request.get(`/business/revenue/delete/${id}`)
}
export function deleteByIdInBatch (ids) {
  return request.get('/business/revenue/delete/batch', {
    params: {
      ids
    }
  })
}
// å¯¼å‡ºExcel
export function exportExcel (data) {
  return request.post('/business/revenue/exportExcel', data, {
    download: true,
    trim: true
  })
}
admin/src/api/business/shopInfo.js
@@ -44,3 +44,8 @@
export function resetPassword (data) {
  return request.post('/business/shopInfo/resetPassword', data)
}
// é—¨åº—审批
export function audit (data) {
  return request.post('/business/shopInfo/audit', data)
}
admin/src/api/business/shopWithdraw.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
import request from '../../utils/request'
export function fetchList (data) {
  return request.post('/business/withdrawalOrders/page', data, {
    trim: true
  })
}
export function getById (id) {
  return request.get(`/business/withdrawalOrders/${id}`)
}
export function getTotalAmount (data) {
  return request.post('/business/withdrawalOrders/totalAmount', data, {
    trim: true
  })
}
export function audit (data) {
  return request.post('/business/withdrawalOrders/audit', data, {
    trim: true
  })
}
export function approve (data) {
  return request.post('/business/withdrawalOrders/approve', data, {
    trim: true
  })
}
admin/src/api/business/sysParams.js
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,11 @@
import request from '../../utils/request'
export function getParams () {
  return request.get('/business/operationConfig')
}
export function saveParams (data) {
  return request.post('/business/operationConfig/save', data, {
    trim: true
  })
}
admin/src/components/business/OperaBannerEditWindow.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,135 @@
<template>
  <GlobalWindow
    :title="title"
    :visible.sync="visible"
    width="500px"
    :confirm-working="isWorking"
    @confirm="confirm"
  >
    <el-form ref="form" :model="form" :rules="rules">
      <el-form-item label="标题" prop="title">
        <el-input v-model="form.title" placeholder="请输入标题"></el-input>
      </el-form-item>
      <el-form-item label="图片" prop="imgurl">
        <UploadImages
          :fileList="imgurl ? [{ url: imgurl }] : []"
          :uploadData="{ folder: 'banner' }"
          :maxCount="1"
          @getFileList="e => { form.imgurl = e.fileurl; imgurl = e.url }"
          @deleteRow="form.imgurl = ''; imgurl = ''" />
      </el-form-item>
      <el-form-item label="排序码" prop="sortnum">
        <el-input v-model="form.sortnum" placeholder="请输入排序码"></el-input>
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-switch
          v-model="form.status"
          active-color="#13ce66"
          inactive-color="#ff4949"
          :active-value="1"
          :inactive-value="0"
        ></el-switch>
      </el-form-item>
      <el-form-item label="展示位置" prop="position">
        <el-select v-model="form.position" placeholder="请选择展示位置">
          <el-option label="会员端首页轮播" :value="0"></el-option>
          <el-option label="司机APP引导页" :value="1"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="跳转方式" prop="type">
        <el-select v-model="form.type" placeholder="请选择跳转方式">
          <el-option label="无" :value="0"></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="内容" prop="content" v-if="form.type === 1">
        <RichEditor :richData="form.content" :styleEditor="styleEditor" @getWangedditor="getWangedditor" :readonly="false"/>
      </el-form-item>
      <el-form-item label="内容" prop="content" v-if="form.type === 2">
        <el-input v-model="form.content" placeholder="请输入内容"></el-input>
      </el-form-item>
    </el-form>
  </GlobalWindow>
</template>
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalWindow from '@/components/common/GlobalWindow'
import UploadImages from '@/components/common/uploadImages'
import { getById } from '@/api/business/banner'
import RichEditor from '@/components/common/RichEditor'
export default {
  name: 'OperaBannerEditWindow',
  extends: BaseOpera,
  components: { GlobalWindow, UploadImages, RichEditor },
  data () {
    return {
      // è¡¨å•数据
      styleEditor:'border: 1px solid #ccc;display: inline-block;',
      form: {
        id: null,
        title: '',
        content: '',
        position: '',
        imgurl: '',
        type: '',
        sortnum: '',
        status: 1
      },
      imgurl: '',
      rules: {
        position: [{ required: true, message: '请选择展示位置', trigger: 'change' }],
        imgurl: [{ required: true, message: '请上传图片', trigger: 'change' }],
        type: [{ required: true, message: '请选择跳转方式', trigger: 'change' }],
        content: [{ required: true, message: '请输入内容', trigger: 'blur' }]
      }
    }
  },
  created () {
    this.config({
      api: '/business/banner',
      'field.id': 'id'
    })
  },
  methods: {
    open (title, row) {
      this.title = title
      if (row && row.id) {
        getById(row.id).then(res => {
          this.form = {
            id: res.id,
            title: res.title || '',
            position: res.position,
            imgurl: res.imgurl || '',
            type: res.type,
            sortnum: res.sortnum,
            status: res.status ?? 1,
            content: res.content || ''
          }
          this.imgurl = res.imgurlFull
          this.visible = true
        }).catch(e => {
          this.$tip.apiFailed(e)
        })
      } else {
        this.form = {
          id: null,
          title: '',
          position: '',
          imgurl: '',
          type: '',
          sortnum: '',
          status: 1,
          content: ''
        }
        this.imgurl = ''
        this.visible = true
      }
    },
    getWangedditor (data) {
      this.form.content = data
    }
  }
}
</script>
admin/src/components/business/OperaGoodsCategoryEditWindow.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,204 @@
<template>
  <GlobalWindow
    :title="title"
    :visible.sync="visible"
    width="500px"
    :confirm-working="isWorking"
    @confirm="confirm"
  >
    <el-form ref="form" :model="form" :rules="rules">
      <el-form-item label="类型名称" prop="name" v-if="form.type === 1">
        <el-input v-model="form.name" placeholder="请输入类型名称"></el-input>
      </el-form-item>
      <el-form-item label="行李类型" prop="name" v-if="form.type === 4">
        <el-input v-model="form.name" placeholder="请输入行李类型"></el-input>
      </el-form-item>
      <el-form-item label="物品名称" prop="name" v-if="form.type === 2">
        <el-input v-model="form.name" placeholder="请输入物品名称"></el-input>
      </el-form-item>
      <el-form-item label="等级名称" prop="name" v-if="form.type === 3">
        <el-input v-model="form.name" placeholder="请输入等级名称"></el-input>
      </el-form-item>
      <el-form-item label="通行方式" prop="detail" v-if="form.type === 1">
        <el-select v-model="form.detail" placeholder="请选择通行方式">
          <el-option label="机动车" value="driving"></el-option>
          <el-option label="非机动车" value="bicycling"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="上传驾驶证" prop="otherField" v-if="form.type === 1">
        <el-radio-group v-model="form.otherField">
          <el-radio label="1">需要</el-radio>
          <el-radio label="0">不需要</el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="是否特大尺寸" prop="detail" v-if="form.type === 4">
        <el-radio-group v-model="form.detail">
          <el-radio label="0">否</el-radio>
          <el-radio label="1">是</el-radio>
        </el-radio-group>
      </el-form-item>
      <el-form-item label="图标" prop="iconFull" v-if="form.type === 4">
        <UploadImages
          :fileList="form.iconFull ? [{ url: form.iconFull }] : []"
          :uploadData="{ folder: 'category' }"
          :maxCount="1"
          @getFileList="e => { form.icon = e.fileurl; form.iconFull = e.url }"
          @deleteRow="form.icon = ''; form.iconFull = ''" />
      </el-form-item>
      <el-form-item label="物品等级" prop="relationId" v-if="form.type === 2">
        <el-select v-model="form.relationId" placeholder="请选择物品等级">
          <el-option
            v-for="item in list"
            :key="item.id"
            :label="item.name"
            :value="item.id" />
        </el-select>
      </el-form-item>
      <el-form-item label="所需司机评级" prop="detail" v-if="form.type === 3">
        <el-select v-model="form.detail" placeholder="请选择司机评级">
          <el-option label="S" :value="5"></el-option>
          <el-option label="A" :value="4"></el-option>
          <el-option label="B" :value="3"></el-option>
          <el-option label="C" :value="2"></el-option>
          <el-option label="D" :value="1"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="寄存说明" prop="detail" v-if="form.type === 2">
        <el-input v-model="form.detail" type="textarea" :rows="3" placeholder="请输入寄存说明"></el-input>
      </el-form-item>
      <el-form-item label="备注说明" prop="remark" v-if="form.type === 3 || form.type === 4">
        <el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入备注说明"></el-input>
      </el-form-item>
      <el-form-item label="排序" prop="sortnum">
        <el-input v-model="form.sortnum" placeholder="请输入排序"></el-input>
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <div style="display: flex; align-items: center;">
          <el-switch
            v-model="form.status"
            active-color="#13ce66"
            inactive-color="#ff4949"
            :active-value="0"
            :inactive-value="1"
          ></el-switch>
          <span v-if="form.type === 2" style="margin-left: 5px; font-size: 12px; color: #909399;">开启后,该城市下门店可被会员查看</span>
        </div>
      </el-form-item>
    </el-form>
  </GlobalWindow>
</template>
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalWindow from '@/components/common/GlobalWindow'
import { getById, fetchList } from '@/api/business/goodsCategory'
import UploadImages from '@/components/common/uploadImages'
export default {
  name: 'OperaGoodsCategoryEditWindow',
  extends: BaseOpera,
  components: { GlobalWindow, UploadImages },
  data () {
    return {
      form: {
        id: null,
        name: '',
        relationId: '',
        detail: '',
        sortnum: '',
        status: 0,
        type: '',
        remark: '',
        icon: '',
        iconFull: '',
        otherField: '1'
      },
      rules: {},
      list: []
    }
  },
  created () {
    this.config({
      api: '/business/goodsCategory',
      'field.id': 'id'
    })
  },
  methods: {
    open (title, row, type) {
      this.title = title
      if (type === 2) {
        fetchList({
          capacity: 99999,
          page: 1,
          model: {
            type: 3
          }
        }).then(res => {
          this.list = res.records || []
        })
      }
      if (row && row.id) {
        getById(row.id).then(res => {
          this.form = {
            id: res.id,
            name: res.name || '',
            relationId: res.relationId || '',
            detail: res.detail || '',
            sortnum: res.sortnum || '',
            status: res.status ?? 0,
            type: res.type ?? 2,
            remark: res.remark || '',
            icon: res.icon || '',
            iconFull: res.iconFull || '',
            otherField: res.otherField || ''
          }
          this.visible = true
        }).catch(e => {
          this.$tip.apiFailed(e)
        })
      } else {
        this.form = {
          id: null,
          name: '',
          relationId: '',
          detail: '',
          sortnum: '',
          status: 0,
          type: '',
          remark: '',
          icon: '',
          iconFull: '',
          otherField: '1'
        }
        if (type === 4) {
          this.form.detail = '0'
        }
        this.form.type = type
        this.visible = true
      }
      if (this.form.type === 2) {
        this.rules = {
          name: [{ required: true, message: '请输入物品名称', trigger: 'blur' }],
          relationId: [{ required: true, message: '请选择物品等级', trigger: 'blur' }]
        }
      } else if (this.form.type === 3) {
        this.rules = {
          name: [{ required: true, message: '请输入等级名称', trigger: 'blur' }],
          detail: [{ required: true, message: '请选择司机评级', trigger: 'blur' }]
        }
      } else if (this.form.type === 4) {
        this.rules = {
          name: [{ required: true, message: '请输入行李类型', trigger: 'blur' }],
          detail: [{ required: true, message: '请选择是否特大尺寸', trigger: 'blur' }],
          icon: [{ required: true, message: '请上传图标', trigger: 'blur' }]
        }
      } else if (this.form.type === 1) {
        this.rules = {
          name: [{ required: true, message: '请输入类型名称', trigger: 'blur' }],
          detail: [{ required: true, message: '请选择通行方式', trigger: 'blur' }],
          otherField: [{ required: true, message: '请选择通行方式', trigger: 'blur' }]
        }
      }
    }
  }
}
</script>
admin/src/components/business/OperaMemberDetail.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,185 @@
<template>
  <GlobalWindow
    :title="title"
    :visible.sync="visible"
    width="600px"
    :withFooter="false"
  >
    <div class="detail-container">
      <div class="section">
        <div class="section-header">
          <span class="section-title">用户信息</span>
          <span class="status-tag">
            <el-tag type="success" v-if="detailInfo.telephone">已授权手机号</el-tag>
            <el-tag type="warning" v-else>未授权手机号</el-tag>
          </span>
        </div>
        <div class="info-grid">
          <div class="info-item">
            <span class="label">微信openid:</span>
            <span class="value">{{ detailInfo.openId }}</span>
          </div>
          <div class="info-item">
            <span class="label">用户昵称:</span>
            <span class="value">{{ detailInfo.nickName }}</span>
          </div>
          <div class="info-item">
            <span class="label">会员姓名:</span>
            <span class="value">{{ detailInfo.name }}</span>
          </div>
          <div class="info-item">
            <span class="label">授权手机号:</span>
            <span class="value">{{ detailInfo.telephone }}</span>
          </div>
          <div class="info-item">
            <span class="label">状态:</span>
            <span class="value">{{ detailInfo.status === 0 ? '正常' : detailInfo.status === 1 ? '停用' : '已注销' }}</span>
          </div>
        </div>
      </div>
    </div>
  </GlobalWindow>
</template>
<script>
import GlobalWindow from '@/components/common/GlobalWindow'
import { getById } from '@/api/business/memberManage'
import BaseOpera from '@/components/base/BaseOpera'
export default {
  name: 'OperaMemberDetail',
  extends: BaseOpera,
  components: { GlobalWindow, BaseOpera },
  data () {
    return {
      detailInfo: {
        id: null,
        openId: '',
        nickName: '',
        name: '',
        phone: '',
        status: 1
      }
    }
  },
  methods: {
    open (title, row) {
      this.title = title
      getById(row.id).then(res => {
        this.detailInfo = {
          id: res.id,
          openId: res.openId,
          nickName: res.nickName,
          name: res.name,
          phone: res.phone,
          status: res.status
        }
        this.visible = true
      }).catch(e => {
        this.$tip.apiFailed(e)
      })
    }
  }
}
</script>
<style scoped>
.detail-container {
  padding: 20px;
}
.section {
  margin-bottom: 30px;
}
.section-header {
  display: flex;
  align-items: center;
  gap: 15px;
  margin-bottom: 15px;
}
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
  padding-left: 10px;
  border-left: 4px solid #2E68EC;
}
.status-tag {
  padding: 4px 12px;
  border-radius: 4px;
  font-size: 12px;
}
.status-pending {
  background: #fdf6ec;
  color: #E6A23C;
}
.status-success {
  background: #f0f9eb;
  color: #67C23A;
}
.status-reject {
  background: #fef0f0;
  color: #F56C6C;
}
.info-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 15px;
  padding: 0 10px;
}
.info-item {
  display: flex;
  font-size: 14px;
}
.info-item .label {
  color: #909399;
  min-width: 90px;
}
.info-item .value {
  color: #606266;
}
.info-item .amount {
  color: #f56c6c;
  font-weight: bold;
}
.info-item.full-width {
  grid-column: span 2;
}
.timeline-content {
  padding: 10px;
  background: #f5f7fa;
  border-radius: 4px;
}
.timeline-title {
  font-size: 14px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 8px;
}
.timeline-info {
  display: flex;
  gap: 20px;
  font-size: 13px;
  color: #606266;
  margin-bottom: 5px;
}
.timeline-remark {
  font-size: 13px;
  color: #909399;
}
.approval-form {
  padding: 20px;
  background: #f5f7fa;
  border-top: 1px solid #eee;
}
.approval-form /deep/ .el-form-item {
  margin-bottom: 15px;
}
.approval-form /deep/ .el-form-item:last-child {
  margin-bottom: 0;
}
.approval-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 15px;
}
</style>
admin/src/components/business/OperaShopApprovalWindow.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,450 @@
<template>
  <GlobalWindow
    :title="title"
    :withFooter="false"
    :visible.sync="visible"
    width="80%"
  >
    <div class="store-header" v-if="storeInfo">
      <div class="store-header-left">
        <el-image :src="storeInfo.payMemberCoverImage ? storeInfo.imgPrefix + storeInfo.payMemberCoverImage : ''" fit="cover" class="store-avatar">
          <div slot="error" class="image-slot">
            <i class="el-icon-picture-outline"></i>
          </div>
        </el-image>
      </div>
      <div class="store-header-right">
        <div class="store-name">{{ storeInfo.name }}</div>
        <div class="store-info-row">
          <span class="info-item">
            <span class="label">门店类型:</span>
            <span class="value">{{ storeInfo.companyType === 1 ? '企业' : '个人' }}</span>
          </span>
          <span class="info-item">
            <span class="label">联系人:</span>
            <span class="value">{{ storeInfo.linkName }}</span>
          </span>
          <span class="info-item">
            <span class="label">联系电话:</span>
            <span class="value">{{ storeInfo.linkPhone }}</span>
          </span>
          <span class="info-item">
            <span class="label">身份证号:</span>
            <span class="value">{{ storeInfo.idcard }}</span>
          </span>
        </div>
      </div>
    </div>
    <div class="qualification-content" v-if="storeInfo">
      <div class="qualification-section">
        <h4 class="section-title">基本信息</h4>
        <div class="info-grid">
          <div class="info-row">
            <span class="label">所在省市区:</span>
            <span class="value">{{ storeInfo.provinceName || '' }} {{ storeInfo.cityName || '' }} {{ storeInfo.areaName || '' }}</span>
          </div>
          <div class="info-row">
            <span class="label">门店地址:</span>
            <span class="value">{{ storeInfo.address }}</span>
          </div>
          <div class="info-row">
            <span class="label">门店状态:</span>
            <span class="value">{{ storeInfo.auditStatus === 0 ? '待审批' : storeInfo.auditStatus === 1 ? '审批通过' : '审批未通过' }}</span>
          </div>
          <div class="info-row">
            <span class="label">配送范围:</span>
            <span class="value">{{ storeInfo.deliveryRange || '暂无' }}</span>
          </div>
        </div>
      </div>
      <div class="qualification-section">
        <h4 class="section-title">主体资质</h4>
        <template v-if="storeInfo.companyType === 1">
          <div class="info-grid">
            <div class="info-row">
              <span class="label">法人姓名:</span>
              <span class="value">{{ storeInfo.legalPersonName }}</span>
            </div>
            <div class="info-row">
              <span class="label">法人手机号:</span>
              <span class="value">{{ storeInfo.legalPersonPhone }}</span>
            </div>
            <div class="info-row">
              <span class="label">法人身份证号码:</span>
              <span class="value">{{ storeInfo.legalPersonCard }}</span>
            </div>
          </div>
          <div class="image-section">
            <div class="image-item">
              <span class="label">法人身份证正面:</span>
              <el-image :src="storeInfo.idcardImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImg]">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
            <div class="image-item">
              <span class="label">法人身份证反面:</span>
              <el-image :src="storeInfo.idcardImgBack" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImgBack]">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
            <div class="image-item">
              <span class="label">营业执照:</span>
              <el-image :src="storeInfo.businessImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.businessImg]">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
          </div>
        </template>
        <template v-else>
          <div class="image-item-row">
            <span class="label">身份证正面:</span>
            <el-image :src="storeInfo.idcardImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImg]">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
          <div class="image-item-row">
            <span class="label">身份证反面:</span>
            <el-image :src="storeInfo.idcardImgBack" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImgBack]">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
          <div class="image-item-row">
            <span class="label">有效劳动合同:</span>
            <div class="image-list">
              <el-image v-for="(img, index) in storeInfo.laborContractImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.laborContractImgs">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
          </div>
          <div class="image-item-row">
            <span class="label">社保缴纳证明:</span>
            <div class="image-list">
              <el-image v-for="(img, index) in storeInfo.socialSecurityImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.socialSecurityImgs">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
          </div>
        </template>
      </div>
      <div class="qualification-section" v-if="storeInfo.companyType === 1">
        <h4 class="section-title">门店照片及其他材料</h4>
        <div class="image-item-row">
          <span class="label">门店门头照:</span>
          <div class="image-list">
            <el-image v-for="(img, index) in storeInfo.storeFrontImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.storeFrontImgs">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
        </div>
        <div class="image-item-row">
          <span class="label">门店内部照片:</span>
          <div class="image-list">
            <el-image v-for="(img, index) in storeInfo.storeInteriorImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.storeInteriorImgs">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
        </div>
        <div class="image-item-row">
          <span class="label">其它材料:</span>
          <div class="image-list">
            <el-image v-for="(img, index) in storeInfo.otherMaterialImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.otherMaterialImgs">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
        </div>
      </div>
    </div>
    <div class="approval-section" v-if="storeInfo && storeInfo.auditStatus !== 0">
      <div class="approval-result" :class="storeInfo.auditStatus === 1 ? 'approval-pass' : 'approval-reject'">
        <div class="approval-status" v-if="storeInfo.auditStatus === 1">
          <i class="el-icon-circle-check"></i>
          <span>审核通过</span>
        </div>
        <div class="approval-status" v-else>
          <i class="el-icon-circle-close"></i>
          <span>审核拒绝</span>
        </div>
        <div class="approval-info" v-if="storeInfo.auditStatus === 1">
          <span class="info-item">审核时间:{{ storeInfo.auditTime }}</span>
          <span class="info-item">审核人:{{ storeInfo.auditName }}</span>
        </div>
        <div class="approval-info" v-else>
          <div class="info-item" v-if="storeInfo.auditRemark">备注:{{ storeInfo.auditRemark }}</div>
          <div class="info-item">审核时间:{{ storeInfo.auditTime }}</div>
          <div class="info-item">审核人:{{ storeInfo.auditName }}</div>
        </div>
      </div>
    </div>
    <div class="approval-form" v-if="storeInfo && storeInfo.auditStatus === 0">
      <el-form ref="approvalForm" :model="approvalForm" :rules="approvalRules">
        <el-form-item label="审批结果" prop="auditStatus">
          <el-radio-group v-model="approvalForm.auditStatus" @change="handleAuditStatusChange">
            <el-radio :label="0">通过</el-radio>
            <el-radio :label="1">拒绝</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="审批意见" prop="auditRemark">
          <el-input
            v-model="approvalForm.auditRemark"
            type="textarea"
            :rows="2"
            :placeholder="approvalForm.auditStatus === 2 ? '请输入拒绝原因(必填)' : '请输入审批意见'"
            style="width: 300px"
          ></el-input>
        </el-form-item>
        <section class="approval-buttons">
          <el-button type="primary" @click="handleSubmit" :loading="isWorking.submit">提交</el-button>
          <el-button @click="handleCancel">取消</el-button>
        </section>
      </el-form>
    </div>
  </GlobalWindow>
</template>
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalWindow from '@/components/common/GlobalWindow'
import { detail, audit } from '@/api/business/shopInfo'
export default {
  name: 'OperaShopApprovalWindow',
  extends: BaseOpera,
  components: { GlobalWindow },
  data () {
    return {
      storeInfo: null,
      approvalForm: {
        id: null,
        auditStatus: 0,
        auditRemark: ''
      },
      approvalRules: {
        auditRemark: [
          { required: true, message: '请输入拒绝原因', trigger: 'blur' }
        ]
      },
      isWorking: {
        submit: false
      }
    }
  },
  created () {
    this.config({
      api: '/business/shopInfo',
      'field.id': 'id'
    })
  },
  methods: {
    open (title, row) {
      this.title = title
      this.approvalForm = {
        id: row.id,
        auditStatus: 1,
        auditRemark: ''
      }
      detail(row.id)
        .then(res => {
          this.storeInfo = res
          this.visible = true
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
    },
    handleAuditStatusChange (val) {
      if (val === 1) {
        this.approvalRules.auditRemark = []
      } else {
        this.approvalRules.auditRemark = [{ required: true, message: '请输入拒绝原因', trigger: 'blur' }]
      }
    },
    handleSubmit () {
      this.$refs.approvalForm.validate(valid => {
        if (!valid) return
        this.isWorking.submit = true
        audit(this.approvalForm)
          .then(res => {
            this.$tip.apiSuccess(res || '提交成功')
            this.visible = false
            this.$emit('success')
          })
          .catch(e => {
            this.$tip.apiFailed(e)
          })
          .finally(() => {
            this.isWorking.submit = false
          })
      })
    },
    handleCancel () {
      this.visible = false
    }
  }
}
</script>
<style scoped>
.store-header {
  display: flex;
  background: #f5f7fa;
  border-radius: 8px;
  padding: 20px;
  margin-bottom: 20px;
}
.store-header-left {
  margin-right: 20px;
}
.store-avatar {
  width: 80px;
  height: 80px;
  border-radius: 50%;
}
.store-header-right {
  flex: 1;
}
.store-name {
  font-size: 18px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 10px;
}
.store-info-row {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}
.info-item {
  font-size: 14px;
  color: #606266;
}
.info-item .label {
  color: #909399;
}
.qualification-content {
  padding: 20px;
}
.qualification-section {
  margin-bottom: 30px;
}
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 15px;
  padding-left: 10px;
  border-left: 4px solid #2E68EC;
}
.info-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 15px;
  margin-bottom: 20px;
}
.info-row {
  display: flex;
  font-size: 14px;
}
.info-row .label {
  color: #909399;
  min-width: 100px;
}
.info-row .value {
  color: #606266;
}
.image-section {
  display: flex;
  flex-wrap: wrap;
  gap: 30px;
}
.image-item {
  display: flex;
  flex-direction: column;
}
.image-item-row {
  display: flex;
  align-items: flex-start;
  margin-bottom: 20px;
}
.image-item-row .label {
  color: #909399;
  font-size: 14px;
  min-width: 120px;
}
.image-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.image-item .label {
  color: #909399;
  font-size: 14px;
  margin-bottom: 8px;
}
.qualification-image {
  width: 150px;
  height: 100px;
  border-radius: 4px;
  border: 1px solid #eee;
}
.image-slot {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  background: #f5f7fa;
  color: #909399;
  font-size: 20px;
}
.approval-section {
  padding: 20px;
}
.approval-result {
  border-radius: 8px;
  padding: 20px;
}
.approval-pass {
  background: #f0f9eb;
  border: 1px solid #67c23a;
}
.approval-reject {
  background: #fef0f0;
  border: 1px solid #f56c6c;
}
.approval-status {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 15px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.approval-pass .approval-status {
  color: #67c23a;
}
.approval-reject .approval-status {
  color: #f56c6c;
}
.approval-info {
  display: flex;
  flex-wrap: wrap;
  gap: 30px;
  font-size: 14px;
  color: #606266;
}
.approval-form {
  padding: 20px;
  border-top: 1px solid #eee;
  background: #f5f7fa;
  border-radius: 8px;
  margin-top: 20px;
}
.approval-buttons {
  margin-top: 20px;
}
</style>
admin/src/components/business/OperaShopEditWindow.vue
@@ -2,11 +2,11 @@
  <GlobalWindow
    title="编辑门店"
    :visible.sync="visible"
    width="60%"
    width="600px"
    :confirm-working="isWorking.save"
    @confirm="handleConfirm"
  >
    <el-form ref="form" :model="form" label-width="120px" :rules="rules" class="inline-form">
    <el-form ref="form" :model="form" :rules="rules">
      <div class="form-section">
        <h4 class="section-title">账号信息</h4>
        <el-form-item label="注册手机号" prop="telephone">
@@ -307,12 +307,17 @@
      this.$refs.form.validate(valid => {
        if (!valid) return
        this.isWorking.save = true
        const imageFields = ['businessImg', 'idcardImg', 'idcardImgBack', 'laborContractImgs', 'socialSecurityImgs', 'storeFrontImgs', 'storeInteriorImgs', 'otherMaterialImgs']
        const imageFields = ['laborContractImgs', 'socialSecurityImgs', 'storeFrontImgs', 'storeInteriorImgs', 'otherMaterialImgs']
        const singleImageFields = ['businessImg', 'idcardImg', 'idcardImgBack']
        const data = { ...this.form }
        singleImageFields.forEach(field => {
          if (data[field] && Array.isArray(data[field])) {
            data[field] = data[field].map(item => typeof item === 'object' ? item.fileurl : item).join(',')
          }
        })
        imageFields.forEach(field => {
          if (data[field]) {
            const list = Array.isArray(data[field]) ? data[field] : [data[field]]
            data[field] = list.map(item => typeof item === 'object' ? item.fileurl : item).join(',')
          if (data[field] && Array.isArray(data[field])) {
            data[field] = data[field].map(item => typeof item === 'object' ? item.fileurl : item)
          }
        })
        data.provinceId = this.form.areaCode[0] || ''
@@ -352,7 +357,7 @@
.password-tip {
  color: #909399;
  font-size: 12px;
  margin: -10px 0 15px 120px;
  margin: -10px 0 15px 0;
}
.longitude-latitude {
  display: flex;
admin/src/components/business/OperaShopInfoSeeWindow.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,416 @@
<template>
  <GlobalWindow
    :title="title"
    :withFooter="false"
    :visible.sync="visible"
    width="80%"
  >
    <div class="store-header" v-if="storeInfo">
      <div class="store-header-left">
        <el-image :src="storeInfo.payMemberCoverImage ? storeInfo.imgPrefix + storeInfo.payMemberCoverImage : ''" fit="cover" class="store-avatar">
          <div slot="error" class="image-slot">
            <i class="el-icon-picture-outline"></i>
          </div>
        </el-image>
      </div>
      <div class="store-header-right">
        <div class="store-name">{{ storeInfo.name }}</div>
        <div class="store-info-row">
          <span class="info-item">
            <span class="label">门店类型:</span>
            <span class="value">{{ storeInfo.companyType === 1 ? '企业' : '个人' }}</span>
          </span>
          <span class="info-item">
            <span class="label">联系人:</span>
            <span class="value">{{ storeInfo.linkName }}</span>
          </span>
          <span class="info-item">
            <span class="label">联系电话:</span>
            <span class="value">{{ storeInfo.linkPhone }}</span>
          </span>
          <span class="info-item">
            <span class="label">身份证号:</span>
            <span class="value">{{ storeInfo.idcard }}</span>
          </span>
        </div>
      </div>
    </div>
    <div class="approval-section" v-if="storeInfo && storeInfo.auditStatus !== 0">
      <div class="approval-result" :class="storeInfo.auditStatus === 1 ? 'approval-pass' : 'approval-reject'">
        <div class="approval-status" v-if="storeInfo.auditStatus === 1">
          <i class="el-icon-circle-check"></i>
          <span>审核通过</span>
        </div>
        <div class="approval-status" v-else>
          <i class="el-icon-circle-close"></i>
          <span>审核拒绝</span>
        </div>
        <div class="approval-info" v-if="storeInfo.auditStatus === 1">
          <span class="info-item">审核时间:{{ storeInfo.auditTime }}</span>
          <span class="info-item">审核人:{{ storeInfo.auditName }}</span>
        </div>
        <div class="approval-info" v-else>
          <div class="info-item" v-if="storeInfo.auditRemark">备注:{{ storeInfo.auditRemark }}</div>
          <div class="info-item">审核时间:{{ storeInfo.auditTime }}</div>
          <div class="info-item">审核人:{{ storeInfo.auditName }}</div>
        </div>
      </div>
    </div>
    <div class="qualification-content" v-if="storeInfo">
      <div class="qualification-section">
        <h4 class="section-title">基本信息</h4>
        <div class="info-grid">
          <div class="info-row">
            <span class="label">所在省市区:</span>
            <span class="value">{{ storeInfo.provinceName || '' }} {{ storeInfo.cityName || '' }} {{ storeInfo.areaName || '' }}</span>
          </div>
          <div class="info-row">
            <span class="label">门店地址:</span>
            <span class="value">{{ storeInfo.address }}</span>
          </div>
          <div class="info-row">
            <span class="label">门店状态:</span>
            <span class="value">{{ storeInfo.auditStatus === 0 ? '待审批' : storeInfo.auditStatus === 1 ? '审批通过' : '审批未通过' }}</span>
          </div>
          <div class="info-row">
            <span class="label">配送范围:</span>
            <span class="value">{{ storeInfo.deliveryRange || '暂无' }}</span>
          </div>
        </div>
      </div>
      <div class="qualification-section">
        <h4 class="section-title">主体资质</h4>
        <template v-if="storeInfo.companyType === 1">
          <div class="info-grid">
            <div class="info-row">
              <span class="label">法人姓名:</span>
              <span class="value">{{ storeInfo.legalPersonName }}</span>
            </div>
            <div class="info-row">
              <span class="label">法人手机号:</span>
              <span class="value">{{ storeInfo.legalPersonPhone }}</span>
            </div>
            <div class="info-row">
              <span class="label">法人身份证号码:</span>
              <span class="value">{{ storeInfo.legalPersonCard }}</span>
            </div>
          </div>
          <div class="image-section">
            <div class="image-item">
              <span class="label">法人身份证正面:</span>
              <el-image :src="storeInfo.idcardImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImg]">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
            <div class="image-item">
              <span class="label">法人身份证反面:</span>
              <el-image :src="storeInfo.idcardImgBack" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImgBack]">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
            <div class="image-item">
              <span class="label">营业执照:</span>
              <el-image :src="storeInfo.businessImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.businessImg]">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
          </div>
        </template>
        <template v-else>
          <div class="image-item-row">
            <span class="label">身份证正面:</span>
            <el-image :src="storeInfo.idcardImg" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImg]">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
          <div class="image-item-row">
            <span class="label">身份证反面:</span>
            <el-image :src="storeInfo.idcardImgBack" fit="cover" class="qualification-image" :preview-src-list="[storeInfo.idcardImgBack]">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
          <div class="image-item-row">
            <span class="label">有效劳动合同:</span>
            <div class="image-list">
              <el-image v-for="(img, index) in storeInfo.laborContractImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.laborContractImgs">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
          </div>
          <div class="image-item-row">
            <span class="label">社保缴纳证明:</span>
            <div class="image-list">
              <el-image v-for="(img, index) in storeInfo.socialSecurityImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.socialSecurityImgs">
                <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
              </el-image>
            </div>
          </div>
        </template>
      </div>
      <div class="qualification-section" v-if="storeInfo.companyType === 1">
        <h4 class="section-title">门店照片及其他材料</h4>
        <div class="image-item-row">
          <span class="label">门店门头照:</span>
          <div class="image-list">
            <el-image v-for="(img, index) in storeInfo.storeFrontImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.storeFrontImgs">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
        </div>
        <div class="image-item-row">
          <span class="label">门店内部照片:</span>
          <div class="image-list">
            <el-image v-for="(img, index) in storeInfo.storeInteriorImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.storeInteriorImgs">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
        </div>
        <div class="image-item-row">
          <span class="label">其它材料:</span>
          <div class="image-list">
            <el-image v-for="(img, index) in storeInfo.otherMaterialImgs" :key="index" :src="img" fit="cover" class="qualification-image" :preview-src-list="storeInfo.otherMaterialImgs">
              <div slot="error" class="image-slot"><i class="el-icon-picture-outline"></i></div>
            </el-image>
          </div>
        </div>
      </div>
    </div>
  </GlobalWindow>
</template>
<script>
import BaseOpera from '@/components/base/BaseOpera'
import GlobalWindow from '@/components/common/GlobalWindow'
import { detail, audit } from '@/api/business/shopInfo'
export default {
  name: 'OperaShopInfoSeeWindow',
  extends: BaseOpera,
  components: { GlobalWindow },
  data () {
    return {
      storeInfo: null,
      approvalForm: {
        id: null,
        auditStatus: 0,
        auditRemark: ''
      },
      approvalRules: {
        auditRemark: [
          { required: true, message: '请输入拒绝原因', trigger: 'blur' }
        ]
      },
      isWorking: {
        submit: false
      }
    }
  },
  created () {
    this.config({
      api: '/business/shopInfo',
      'field.id': 'id'
    })
  },
  methods: {
    open (title, row) {
      this.title = title
      this.approvalForm = {
        id: row.id,
        auditStatus: 1,
        auditRemark: ''
      }
      detail(row.id)
        .then(res => {
          this.storeInfo = res
          this.visible = true
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
    },
    handleAuditStatusChange (val) {
      if (val === 1) {
        this.approvalRules.auditRemark = []
      } else {
        this.approvalRules.auditRemark = [{ required: true, message: '请输入拒绝原因', trigger: 'blur' }]
      }
    },
    handleSubmit () {
      this.$refs.approvalForm.validate(valid => {
        if (!valid) return
        this.isWorking.submit = true
        audit(this.approvalForm)
          .then(res => {
            this.$tip.apiSuccess(res || '提交成功')
            this.visible = false
            this.$emit('success')
          })
          .catch(e => {
            this.$tip.apiFailed(e)
          })
          .finally(() => {
            this.isWorking.submit = false
          })
      })
    },
    handleCancel () {
      this.visible = false
    }
  }
}
</script>
<style scoped>
.store-header {
  display: flex;
  background: #f5f7fa;
  border-radius: 8px;
  padding: 20px;
  margin-bottom: 20px;
}
.store-header-left {
  margin-right: 20px;
}
.store-avatar {
  width: 80px;
  height: 80px;
  border-radius: 50%;
}
.store-header-right {
  flex: 1;
}
.store-name {
  font-size: 18px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 10px;
}
.store-info-row {
  display: flex;
  flex-wrap: wrap;
  gap: 20px;
}
.info-item {
  font-size: 14px;
  color: #606266;
}
.info-item .label {
  color: #909399;
}
.qualification-content {
  padding: 20px;
}
.qualification-section {
  margin-bottom: 30px;
}
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 15px;
  padding-left: 10px;
  border-left: 4px solid #2E68EC;
}
.info-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 15px;
  margin-bottom: 20px;
}
.info-row {
  display: flex;
  font-size: 14px;
}
.info-row .label {
  color: #909399;
  min-width: 100px;
}
.info-row .value {
  color: #606266;
}
.image-section {
  display: flex;
  flex-wrap: wrap;
  gap: 30px;
}
.image-item {
  display: flex;
  flex-direction: column;
}
.image-item-row {
  display: flex;
  align-items: flex-start;
  margin-bottom: 20px;
}
.image-item-row .label {
  color: #909399;
  font-size: 14px;
  min-width: 120px;
}
.image-list {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}
.image-item .label {
  color: #909399;
  font-size: 14px;
  margin-bottom: 8px;
}
.qualification-image {
  width: 150px;
  height: 100px;
  border-radius: 4px;
  border: 1px solid #eee;
}
.image-slot {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100%;
  height: 100%;
  background: #f5f7fa;
  color: #909399;
  font-size: 20px;
}
.approval-section {
  padding: 20px;
}
.approval-result {
  border-radius: 8px;
  padding: 20px;
}
.approval-pass {
  background: #f0f9eb;
  border: 1px solid #67c23a;
}
.approval-reject {
  background: #fef0f0;
  border: 1px solid #f56c6c;
}
.approval-status {
  font-size: 18px;
  font-weight: bold;
  margin-bottom: 15px;
  display: flex;
  align-items: center;
  gap: 10px;
}
.approval-pass .approval-status {
  color: #67c23a;
}
.approval-reject .approval-status {
  color: #f56c6c;
}
.approval-info {
  display: flex;
  flex-wrap: wrap;
  gap: 30px;
  font-size: 14px;
  color: #606266;
}
</style>
admin/src/components/business/OperaShopInfoWindow.vue
@@ -1,16 +1,17 @@
<template>
  <GlobalWindow
    :title="title"
    :withFooter="false"
    :visible.sync="visible"
    width="80%"
  >
    <div class="store-header" v-if="storeInfo">
      <div class="store-header-left">
        <!-- <el-image :src="storeInfo.headImage || defaultAvatar" fit="cover" class="store-avatar">
        <el-image :src="storeInfo.payMemberCoverImage ? storeInfo.imgPrefix + storeInfo.payMemberCoverImage : ''" fit="cover" class="store-avatar">
          <div slot="error" class="image-slot">
            <i class="el-icon-picture-outline"></i>
          </div>
        </el-image> -->
        </el-image>
      </div>
      <div class="store-header-right">
        <div class="store-name">{{ storeInfo.name }}</div>
@@ -38,14 +39,14 @@
    <el-tabs v-model="activeTab" class="store-tabs">
      <el-tab-pane label="门店业绩" name="performance">
        <el-form ref="searchForm" :model="searchForm" inline>
          <el-form-item label="交易号" prop="transactionId">
            <el-input v-model="searchForm.transactionId" clearable placeholder="请输入交易号"></el-input>
          <el-form-item label="交易号" prop="orderNo">
            <el-input v-model="searchForm.orderNo" clearable placeholder="请输入交易号"></el-input>
          </el-form-item>
          <el-form-item label="收支类型" prop="type">
            <el-select v-model="searchForm.type" clearable placeholder="请选择类型">
              <el-option label="全部" :value="0"></el-option>
          <el-form-item label="收支类型" prop="optType">
            <el-select v-model="searchForm.optType" clearable placeholder="请选择类型">
              <el-option label="全部" value=""></el-option>
              <el-option label="收入" :value="1"></el-option>
              <el-option label="支出" :value="2"></el-option>
              <el-option label="支出" :value="-1"></el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="收支时间" prop="createTime">
@@ -62,26 +63,32 @@
        <el-table :data="tableData.list" border stripe v-loading="isWorking.search" class="performance-table">
          <el-table-column label="收入/支出" min-width="100px">
            <template slot-scope="{row}">
              <span :class="row.type === 1 ? 'income' : 'expense'">{{ row.type === 1 ? '收入' : '支出' }}</span>
              <span :class="row.optType === 1 ? 'income' : 'expense'">{{ row.optType === 1 ? '收入' : '支出' }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="amount" label="金额(元)" min-width="100px">
            <template slot-scope="{row}">
              <span :class="row.type === 1 ? 'income' : 'expense'">{{ row.amount }}</span>
              <span :class="row.optType === 1 ? 'income' : 'expense'">{{ row.amount }}</span>
            </template>
          </el-table-column>
          <el-table-column prop="createTime" label="收支时间" min-width="160px"></el-table-column>
          <el-table-column prop="businessType" label="业务类型" min-width="100px"></el-table-column>
          <el-table-column prop="transactionId" label="交易号" min-width="180px"></el-table-column>
          <el-table-column label="业务类型" min-width="100px">
            <template slot-scope="{row}">
              <span v-if="row.type === 0">完成订单</span>
              <span v-if="row.type === 1">提现支出</span>
              <span v-if="row.type === 2">提现退回</span>
              <span v-if="row.type === 3">平台奖励</span>
            </template>
          </el-table-column>
          <el-table-column prop="orderNo" label="交易号" min-width="180px"></el-table-column>
          <el-table-column label="状态" min-width="100px">
            <template slot-scope="{row}">
              <span :class="row.status === 1 ? 'status-success' : 'status-pending'">
                {{ row.status === 1 ? '已到账' : '提现中' }}
              <span :class="row.vaildStatus === 1 ? 'status-success' : 'status-pending'">
                {{ row.vaildStatus === 1 ? '已到账' : '提现中' }}
              </span>
            </template>
          </el-table-column>
        </el-table>
        <pagination
          @size-change="handleSizeChange"
          @current-change="handlePageChange"
@@ -221,6 +228,7 @@
import GlobalWindow from '@/components/common/GlobalWindow'
import Pagination from '@/components/common/Pagination'
import { detail } from '@/api/business/shopInfo'
import { fetchList, exportExcel } from '@/api/business/revenue'
export default {
  name: 'OperaShopInfoWindow',
  extends: BaseOpera,
@@ -230,9 +238,11 @@
      activeTab: 'performance',
      storeInfo: null,
      searchForm: {
        transactionId: '',
        type: 0,
        createTime: ''
        orderNo: '',
        optType: 0,
        createTime: '',
        memberId: '',
        memberType: 2
      },
      tableData: {
        list: [],
@@ -257,14 +267,17 @@
  },
  methods: {
    open (title, row) {
      this.searchForm.memberId = row.id
      detail(row.id)
        .then(res => {
          this.storeInfo = res
          this.activeTab = 'performance'
          this.searchForm = {
            transactionId: '',
            type: 0,
            createTime: ''
            orderNo: '',
            optType: '',
            createTime: '',
            memberId: '',
            memberType: 2
          }
          this.title = title
          this.visible = true
@@ -277,15 +290,18 @@
    search () {
      this.isWorking.search = true
      const data = {
        pageIndex: this.tableData.pagination.pageIndex,
        pageSize: this.tableData.pagination.pageSize,
        shopId: this.storeInfo?.id,
        transactionId: this.searchForm.transactionId,
        type: this.searchForm.type,
        capacity: this.tableData.pagination.pageSize,
        page: this.tableData.pagination.pageIndex,
        model: {
          orderNo: this.searchForm.orderNo,
          optType: this.searchForm.optType,
        startTime: this.searchForm.createTime?.[0] || '',
        endTime: this.searchForm.createTime?.[1] || ''
          endTime: this.searchForm.createTime?.[1] || '',
          memberId: this.searchForm.memberId,
          memberType: this.searchForm.memberType || ''
      }
      this.api.fetchPerformance(data)
      }
      fetchList(data)
        .then(res => {
          this.tableData.list = res.list || []
          this.tableData.pagination.total = res.total || 0
@@ -299,9 +315,11 @@
    },
    reset () {
      this.searchForm = {
        transactionId: '',
        type: 0,
        createTime: ''
        orderNo: '',
        optType: '',
        createTime: '',
        memberId: '',
        memberType: 2
      }
      this.search()
    },
@@ -316,14 +334,20 @@
    exportExcel () {
      this.isWorking.export = true
      const data = {
        shopId: this.storeInfo?.id,
        transactionId: this.searchForm.transactionId,
        type: this.searchForm.type,
        capacity: 999999,
        page: this.tableData.pagination.pageIndex,
        model: {
          orderNo: this.searchForm.orderNo,
          optType: this.searchForm.optType,
        startTime: this.searchForm.createTime?.[0] || '',
        endTime: this.searchForm.createTime?.[1] || ''
          endTime: this.searchForm.createTime?.[1] || '',
          memberId: this.searchForm.memberId,
          memberType: this.searchForm.memberType || ''
      }
      this.api.exportPerformance(data)
      }
      exportExcel(data)
        .then(res => {
          this.download(res)
          this.$tip.apiSuccess('导出成功')
        })
        .catch(e => {
admin/src/components/business/OperaWithdrawDetailWindow.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,297 @@
<template>
  <GlobalWindow
    :title="title"
    :visible.sync="visible"
    :withFooter="false"
    width="70%"
    :confirm-working="isWorking.submit"
    @confirm="handleConfirm"
  >
    <div class="detail-container">
      <div class="section">
        <div class="section-header">
          <span class="section-title">基本信息</span>
          <span class="status-tag" :class="getStatusClass(detailInfo.status)">
            {{ getStatusText(detailInfo.status) }}
          </span>
        </div>
        <div class="info-grid">
          <div class="info-item">
            <span class="label">门店名称:</span>
            <span class="value">{{ detailInfo.shopName }}</span>
          </div>
          <div class="info-item">
            <span class="label">门店联系人:</span>
            <span class="value">{{ detailInfo.linkName }}</span>
          </div>
          <div class="info-item">
            <span class="label">申请时间:</span>
            <span class="value">{{ detailInfo.createTime }}</span>
          </div>
        </div>
      </div>
      <div class="section">
        <div class="section-header">
          <span class="section-title">提现信息</span>
        </div>
        <div class="info-grid">
          <div class="info-item">
            <span class="label">提现金额:</span>
            <span class="value amount">Â¥{{ detailInfo.amount / 100 }}</span>
          </div>
          <div class="info-item">
            <span class="label">提现账户:</span>
            <span class="value">{{ detailInfo.aliAccount }}</span>
          </div>
          <div class="info-item">
            <span class="label">审核人:</span>
            <span class="value">{{ detailInfo.updateUserName }}</span>
          </div>
          <div class="info-item">
            <span class="label">审核时间:</span>
            <span class="value">{{ detailInfo.updateTime }}</span>
          </div>
          <div class="info-item full-width">
            <span class="label">审核备注:</span>
            <span class="value">{{ detailInfo.approveRemark }}</span>
          </div>
        </div>
      </div>
      <div class="section" v-if="detailInfo.status !== 0">
        <div class="section-header">
          <span class="section-title">审批信息</span>
        </div>
        <div class="info-grid">
          <div class="info-item">
            <span class="label">审核人:</span>
            <span class="value">{{ detailInfo.updateUserName }}</span>
          </div>
          <div class="info-item">
            <span class="label">审核时间:</span>
            <span class="value">{{ detailInfo.updateTime }}</span>
          </div>
          <div class="info-item full-width">
            <span class="label">审核备注:</span>
            <span class="value">{{ detailInfo.approveRemark }}</span>
          </div>
        </div>
      </div>
    </div>
    <div class="approval-form" v-if="detailInfo.status === 0">
      <el-form ref="approvalForm" :model="approvalForm" :rules="approvalRules">
        <el-form-item label="审批结果" prop="status">
          <el-radio-group v-model="approvalForm.status">
            <el-radio :label="1">通过</el-radio>
            <el-radio :label="2">驳回</el-radio>
          </el-radio-group>
        </el-form-item>
        <el-form-item label="备注" prop="approveRemark">
          <el-input
            v-model="approvalForm.approveRemark"
            type="textarea"
            :rows="3"
            placeholder="请输入备注"
            style="width: 100%"
          ></el-input>
        </el-form-item>
      </el-form>
      <div class="approval-buttons">
        <el-button @click="visible = false">取消</el-button>
        <el-button type="primary" @click="handleConfirm" :loading="isWorking.submit">确定</el-button>
      </div>
    </div>
  </GlobalWindow>
</template>
<script>
import GlobalWindow from '@/components/common/GlobalWindow'
import { getById, approve } from '@/api/business/shopWithdraw'
export default {
  name: 'OperaWithdrawDetailWindow',
  components: { GlobalWindow },
  data () {
    return {
      title: '提现详情',
      visible: false,
      detailInfo: {
        status: '',
        shopName: '',
        linkName: '',
        createTime: '',
        amount: '',
        bankCardNo: '',
        auditLogs: []
      },
      approvalForm: {
        id: null,
        status: 1,
        approveRemark: ''
      },
      approvalRules: {
        status: [{ required: true, message: '请选择审批结果', trigger: 'change' }]
      },
      isWorking: {
        submit: false
      }
    }
  },
  methods: {
    open (title, row) {
      this.title = title
      this.approvalForm.id = row.id
      this.approvalForm.status = 1
      this.approvalForm.approveRemark = ''
      getById(row.id).then(res => {
        this.detailInfo = {
          status: res.status,
          shopName: res.shopName,
          linkName: res.linkName,
          createTime: res.createTime,
          amount: res.amount,
          bankCardNo: res.bankCardNo,
          auditLogs: res.auditLogs || [],
          updateUserName: res.updateUserName,
          updateTime: res.updateTime,
          approveRemark: res.approveRemark
        }
        this.visible = true
      }).catch(e => {
        this.$tip.apiFailed(e)
      })
    },
    getStatusText (status) {
      const map = { 0: '提现申请中', 1: '提现成功', 2: '提现失败' }
      return map[status] || '-'
    },
    getStatusClass (status) {
      const map = { 0: 'status-pending', 1: 'status-success', 2: 'status-reject' }
      return map[status] || ''
    },
    handleConfirm () {
      if (this.detailInfo.status !== 0) {
        this.visible = false
        return
      }
      this.$refs.approvalForm.validate(valid => {
        if (!valid) return
        this.isWorking.submit = true
        approve(this.approvalForm).then(res => {
          this.$tip.apiSuccess(res || '提交成功')
          this.visible = false
          this.$emit('success')
        }).catch(e => {
          this.$tip.apiFailed(e)
        }).finally(() => {
          this.isWorking.submit = false
        })
      })
    }
  }
}
</script>
<style scoped>
.detail-container {
  padding: 20px;
}
.section {
  margin-bottom: 30px;
}
.section-header {
  display: flex;
  align-items: center;
  gap: 15px;
  margin-bottom: 15px;
}
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
  padding-left: 10px;
  border-left: 4px solid #2E68EC;
}
.status-tag {
  padding: 4px 12px;
  border-radius: 4px;
  font-size: 12px;
}
.status-pending {
  background: #fdf6ec;
  color: #E6A23C;
}
.status-success {
  background: #f0f9eb;
  color: #67C23A;
}
.status-reject {
  background: #fef0f0;
  color: #F56C6C;
}
.info-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 15px;
  padding: 0 10px;
}
.info-item {
  display: flex;
  font-size: 14px;
}
.info-item .label {
  color: #909399;
  min-width: 90px;
}
.info-item .value {
  color: #606266;
}
.info-item .amount {
  color: #f56c6c;
  font-weight: bold;
}
.info-item.full-width {
  grid-column: span 2;
}
.timeline-content {
  padding: 10px;
  background: #f5f7fa;
  border-radius: 4px;
}
.timeline-title {
  font-size: 14px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 8px;
}
.timeline-info {
  display: flex;
  gap: 20px;
  font-size: 13px;
  color: #606266;
  margin-bottom: 5px;
}
.timeline-remark {
  font-size: 13px;
  color: #909399;
}
.approval-form {
  padding: 20px;
  background: #f5f7fa;
  border-top: 1px solid #eee;
}
.approval-form /deep/ .el-form-item {
  margin-bottom: 15px;
}
.approval-form /deep/ .el-form-item:last-child {
  margin-bottom: 0;
}
.approval-buttons {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
  margin-top: 15px;
}
</style>
admin/src/views/business/baggageType.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
<template>
  <TableLayout :permissions="['business:category:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="名称" prop="name">
        <el-input v-model="searchForm.name" clearable placeholder="请输入名称" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <ul class="toolbar">
        <li><el-button type="primary" @click="$refs.operaGoodsCategoryEditWindow.open('新建物品等级', null, 4)" icon="el-icon-plus">新建</el-button></li>
      </ul>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="id" label="ID" min-width="80px"></el-table-column>
        <el-table-column prop="name" label="行李类型" min-width="120px"></el-table-column>
        <el-table-column prop="remark" label="备注说明" min-width="200px"></el-table-column>
        <el-table-column prop="sortnum" label="排序" min-width="100px"></el-table-column>
        <el-table-column prop="createTime" label="提交时间" min-width="100px"></el-table-column>
        <el-table-column label="状态" min-width="80px">
          <template slot-scope="{row}">
            <el-switch
              @change="handleStatusChange($event, row)"
              v-model="row.status"
              active-color="#13ce66"
              inactive-color="#ff4949"
              :active-value="0"
              :inactive-value="1"
            ></el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="150" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleEdit(row)">编辑</el-button>
            <el-button type="text" @click="deleteById(row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
    <OperaGoodsCategoryEditWindow ref="operaGoodsCategoryEditWindow" @success="search" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaGoodsCategoryEditWindow from '@/components/business/OperaGoodsCategoryEditWindow'
import { updateStatus } from '@/api/business/goodsCategory'
export default {
  name: 'BaggageTypeList',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaGoodsCategoryEditWindow },
  data () {
    return {
      searchForm: {
        name: '',
        type: 4
      }
    }
  },
  created () {
    this.config({
      api: '/business/goodsCategory',
      'field.id': 'id'
    })
    this.search()
  },
  methods: {
    handleEdit (row) {
      this.$refs.operaGoodsCategoryEditWindow.open('编辑物品等级', row, 4)
    },
    handleStatusChange (val, row) {
      updateStatus({ id: row.id, status: val }).then(res => {
        this.$tip.apiSuccess(res || '修改成功')
      }).catch(e => {
        row.status = val === 1 ? 0 : 1
        this.$tip.apiFailed(e)
      })
    }
  }
}
</script>
admin/src/views/business/bannerList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,138 @@
<template>
  <TableLayout :permissions="['business:banner:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="标题" prop="title">
        <el-input v-model="searchForm.title" clearable placeholder="请输入标题" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <ul class="toolbar" v-permissions="['business:banner:create']">
        <li><el-button type="primary" @click="$refs.operaBannerEditWindow.open('新建轮播图',null,searchForm.type)" icon="el-icon-plus" v-permissions="['business:banner:create']">新建</el-button></li>
      </ul>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="title" label="标题" min-width="120px"></el-table-column>
        <el-table-column prop="position" label="展示位置" min-width="100px">
          <template slot-scope="{row}">
            {{ positionOptions[row.position] || '-' }}
          </template>
        </el-table-column>
        <el-table-column label="图片" min-width="120px">
          <template slot-scope="{row}">
            <el-image
              v-if="row.imgurlFull"
              style="width: 80px; height: 40px"
              :src="row.imgurlFull"
              fit="contain"
            ></el-image>
            <span v-else>-</span>
          </template>
        </el-table-column>
        <el-table-column label="跳转方式" min-width="100px">
          <template slot-scope="{row}">
            {{ jumpTypeOptions[row.type] || '-' }}
          </template>
        </el-table-column>
        <el-table-column prop="sortnum" label="排序码" min-width="80px"></el-table-column>
        <el-table-column label="状态" min-width="80px">
          <template slot-scope="{row}">
            <el-switch
              @change="handleStatusChange($event, row)"
              v-model="row.status"
              active-color="#13ce66"
              inactive-color="#ff4949"
              :active-value="1"
              :inactive-value="0"
            ></el-switch>
          </template>
        </el-table-column>
        <el-table-column prop="updateTime" label="修改时间" min-width="160px"></el-table-column>
        <el-table-column label="操作" min-width="150" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleEdit(row)">编辑</el-button>
            <el-button type="text" @click="deleteById(row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
    <OperaBannerEditWindow ref="operaBannerEditWindow" @success="search" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaBannerEditWindow from '@/components/business/OperaBannerEditWindow'
import { updateById, deleteById } from '@/api/business/banner'
export default {
  name: 'BannerList',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaBannerEditWindow },
  data () {
    return {
      positionOptions: {
        0: '首页',
        1: '店铺首页'
      },
      jumpTypeOptions: {
        0: '无',
        1: '会员端首页轮播',
        2: '司机APP引导页'
      },
      searchForm: {
        title: ''
      }
    }
  },
  created () {
    this.config({
      api: '/business/banner',
      'field.id': 'id'
    })
    this.search()
  },
  methods: {
    loadTableData (resolve) {
      this.isWorking.search = true
      list(this.getTableParams())
        .then(data => {
          this.tableData.list = data.list || []
          this.tableData.pagination.total = data.total || 0
          if (resolve) resolve(data)
        })
        .catch(e => {
          this.$tip.apiFailed(e, '加载失败')
        })
        .finally(() => {
          this.isWorking.search = false
        })
    },
    handleEdit (row) {
      this.$refs.operaBannerEditWindow.open('编辑轮播图', row)
    },
    handleStatusChange (val, row) {
      updateById({ id: row.id, status: val }).then(res => {
        this.$tip.apiSuccess(res || '修改成功')
      }).catch(e => {
        row.status = val === 1 ? 0 : 1
        this.$tip.apiFailed(e)
      })
    }
  }
}
</script>
admin/src/views/business/driverList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,167 @@
<template>
  <TableLayout :permissions="['business:driver:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="司机信息" prop="keyword">
        <el-input v-model="searchForm.keyword" clearable placeholder="请输入司机姓名/手机号" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="车牌号" prop="carNo">
        <el-input v-model="searchForm.carNo" clearable placeholder="请输入车牌号" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="searchForm.status" clearable placeholder="请选择状态" @change="search">
          <el-option label="禁用" :value="0"></el-option>
          <el-option label="启用" :value="1"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="性别" prop="sex">
        <el-select v-model="searchForm.sex" clearable placeholder="请选择性别" @change="search">
          <el-option label="男" :value="1"></el-option>
          <el-option label="女" :value="2"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="创建时间" prop="createTime">
        <el-date-picker type="daterange" v-model="searchForm.createTime" clearable value-format="yyyy-MM-dd"
                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="handleDateChange" />
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
        <el-button :loading="isWorking.export" @click="handleExport">导出</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="nickName" label="用户昵称" min-width="100px"></el-table-column>
        <el-table-column prop="name" label="司机姓名" min-width="100px"></el-table-column>
        <el-table-column label="性别" min-width="60px">
          <template slot-scope="{row}">{{ row.sex === 1 ? '男' : row.sex === 2 ? '女' : '-' }}</template>
        </el-table-column>
        <el-table-column prop="phone" label="注册手机号" min-width="120px"></el-table-column>
        <el-table-column prop="idCard" label="身份证号" min-width="160px"></el-table-column>
        <el-table-column label="账户余额" min-width="100px">
          <template slot-scope="{row}">Â¥{{ (row.amount / 100).toFixed(2) }}</template>
        </el-table-column>
        <el-table-column prop="carNo" label="车牌号" min-width="100px"></el-table-column>
        <el-table-column prop="createTime" label="创建时间" min-width="160px"></el-table-column>
        <el-table-column label="状态" min-width="80px">
          <template slot-scope="{row}">
            <el-switch
              @change="handleStatusChange($event, row)"
              v-model="row.status"
              active-color="#13ce66"
              inactive-color="#ff4949"
              :active-value="1"
              :inactive-value="0"
            ></el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="100" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleDetail(row)">详情</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import { fetchList, updateStatus, exportExcel } from '@/api/business/driver'
export default {
  name: 'DriverList',
  extends: BaseTable,
  components: { TableLayout, Pagination },
  data () {
    return {
      searchForm: {
        keyword: '',
        carNo: '',
        status: '',
        sex: '',
        createTime: '',
        startTime: '',
        endTime: ''
      }
    }
  },
  created () {
    this.config({
      api: '/business/driver',
      'field.id': 'id'
    })
    this.search()
  },
  methods: {
    loadTableData (resolve) {
      this.isWorking.search = true
      fetchList(this.getTableParams())
        .then(data => {
          this.tableData.list = data.list || []
          this.tableData.pagination.total = data.total || 0
          if (resolve) resolve(data)
        })
        .catch(e => {
          this.$tip.apiFailed(e, '加载失败')
        })
        .finally(() => {
          this.isWorking.search = false
        })
    },
    handleDateChange (val) {
      this.searchForm.startTime = val ? val[0] : ''
      this.searchForm.endTime = val ? val[1] : ''
      this.search()
    },
    reset () {
      this.searchForm = {
        keyword: '',
        carNo: '',
        status: '',
        sex: '',
        createTime: '',
        startTime: '',
        endTime: ''
      }
      this.search()
    },
    handleDetail (row) {
    },
    handleStatusChange (val, row) {
      updateStatus({ id: row.id, status: val }).then(res => {
        this.$tip.apiSuccess(res || '修改成功')
      }).catch(e => {
        row.status = val === 1 ? 0 : 1
        this.$tip.apiFailed(e)
      })
    },
    handleExport () {
      this.isWorking.export = true
      exportExcel(this.getTableParams())
        .then(res => {
          this.download(res)
          this.$tip.apiSuccess('导出成功')
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
        .finally(() => {
          this.isWorking.export = false
        })
    }
  }
}
</script>
admin/src/views/business/goodsCategoryList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
<template>
  <TableLayout :permissions="['business:category:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="名称" prop="name">
        <el-input v-model="searchForm.name" clearable placeholder="请输入名称" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <ul class="toolbar">
        <li><el-button type="primary" @click="$refs.operaGoodsCategoryEditWindow.open('新建物品分类', null, 2)" icon="el-icon-plus">新建</el-button></li>
      </ul>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="id" label="ID" min-width="80px"></el-table-column>
        <el-table-column prop="name" label="物品名称" min-width="120px"></el-table-column>
        <el-table-column prop="detail" label="寄存说明" min-width="200px"></el-table-column>
        <el-table-column prop="sortnum" label="分类排序" min-width="100px"></el-table-column>
        <el-table-column label="状态" min-width="80px">
          <template slot-scope="{row}">
            <el-switch
              @change="handleStatusChange($event, row)"
              v-model="row.status"
              active-color="#13ce66"
              inactive-color="#ff4949"
              :active-value="0"
              :inactive-value="1"
            ></el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="150" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleEdit(row)">编辑</el-button>
            <el-button type="text" @click="deleteById(row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
    <OperaGoodsCategoryEditWindow ref="operaGoodsCategoryEditWindow" @success="search" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaGoodsCategoryEditWindow from '@/components/business/OperaGoodsCategoryEditWindow'
import { fetchList, updateStatus, deleteById } from '@/api/business/goodsCategory'
export default {
  name: 'GoodsCategoryList',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaGoodsCategoryEditWindow },
  data () {
    return {
      searchForm: {
        name: '',
        status: '',
        type: 2
      }
    }
  },
  created () {
    this.config({
      api: '/business/goodsCategory',
      'field.id': 'id'
    })
    this.search()
  },
  methods: {
    handleEdit (row) {
      this.$refs.operaGoodsCategoryEditWindow.open('编辑物品分类', row, 2)
    },
    handleStatusChange (val, row) {
      updateStatus({ id: row.id, status: val }).then(res => {
        this.$tip.apiSuccess(res || '修改成功')
      }).catch(e => {
        row.status = val === 1 ? 0 : 1
        this.$tip.apiFailed(e)
      })
    }
  }
}
</script>
admin/src/views/business/itemLevel.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,96 @@
<template>
  <TableLayout :permissions="['business:category:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="名称" prop="name">
        <el-input v-model="searchForm.name" clearable placeholder="请输入名称" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <ul class="toolbar">
        <li><el-button type="primary" @click="$refs.operaGoodsCategoryEditWindow.open('新建物品等级', null, 3)" icon="el-icon-plus">新建</el-button></li>
      </ul>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="id" label="ID" min-width="80px"></el-table-column>
        <el-table-column prop="name" label="等级名称" min-width="120px"></el-table-column>
        <el-table-column prop="detail" label="备注说明" min-width="200px"></el-table-column>
        <el-table-column prop="sortnum" label="排序" min-width="100px"></el-table-column>
        <el-table-column prop="createTime" label="提交时间" min-width="100px"></el-table-column>
        <el-table-column label="状态" min-width="80px">
          <template slot-scope="{row}">
            <el-switch
              @change="handleStatusChange($event, row)"
              v-model="row.status"
              active-color="#13ce66"
              inactive-color="#ff4949"
              :active-value="0"
              :inactive-value="1"
            ></el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="150" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleEdit(row)">编辑</el-button>
            <el-button type="text" @click="deleteById(row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
    <OperaGoodsCategoryEditWindow ref="operaGoodsCategoryEditWindow" @success="search" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaGoodsCategoryEditWindow from '@/components/business/OperaGoodsCategoryEditWindow'
import { updateStatus } from '@/api/business/goodsCategory'
export default {
  name: 'ItemLevelList',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaGoodsCategoryEditWindow },
  data () {
    return {
      searchForm: {
        name: '',
        type: 3
      }
    }
  },
  created () {
    this.config({
      api: '/business/goodsCategory',
      'field.id': 'id'
    })
    this.search()
  },
  methods: {
    handleEdit (row) {
      this.$refs.operaGoodsCategoryEditWindow.open('编辑物品等级', row, 3)
    },
    handleStatusChange (val, row) {
      updateStatus({ id: row.id, status: val }).then(res => {
        this.$tip.apiSuccess(res || '修改成功')
      }).catch(e => {
        row.status = val === 1 ? 0 : 1
        this.$tip.apiFailed(e)
      })
    }
  }
}
</script>
admin/src/views/business/memberList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
<template>
  <TableLayout :permissions="['business:member:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="会员信息" prop="keyword">
        <el-input v-model="searchForm.keyword" clearable placeholder="请输入姓名/手机号" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="searchForm.status" clearable placeholder="请选择状态" @change="search">
          <el-option label="正常" :value="0"></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="创建时间" prop="createTime">
        <el-date-picker type="daterange" v-model="searchForm.createTime" clearable value-format="yyyy-MM-dd"
                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="handleDateChange" />
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
        <el-button :loading="isWorking.export" @click="exportExcel">导出</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="nickName" label="用户昵称" min-width="120px"></el-table-column>
        <el-table-column prop="name" label="会员姓名" min-width="120px"></el-table-column>
        <el-table-column prop="telephone" label="授权手机号" min-width="130px"></el-table-column>
        <el-table-column prop="createTime" label="创建时间" min-width="160px"></el-table-column>
        <el-table-column label="状态" min-width="80px">
          <template slot-scope="{row}">
            {{ row.status === 0 ? '正常' : row.status === 1 ? '停用' : '已注销' }}
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="100" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleDetail(row)">详情</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
    <OperaMemberDetailWindow ref="operaMemberDetailWindow" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaMemberDetailWindow from '@/components/business/OperaMemberDetail'
export default {
  name: 'MemberList',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaMemberDetailWindow },
  data () {
    return {
      searchForm: {
        keyword: '',
        status: '',
        createTime: '',
        startTime: '',
        endTime: ''
      }
    }
  },
  created () {
    this.config({
      api: '/business/memberManage',
      'field.id': 'id'
    })
    this.search()
  },
  methods: {
    handleDateChange (val) {
      this.searchForm.startTime = val ? val[0] : ''
      this.searchForm.endTime = val ? val[1] : ''
      this.search()
    },
    reset () {
      this.searchForm = {
        keyword: '',
        status: '',
        createTime: '',
        startTime: '',
        endTime: ''
      }
      this.search()
    },
    handleDetail (row) {
      this.$refs.operaMemberDetailWindow.open('会员详情', row)
    }
  }
}
</script>
admin/src/views/business/memberManage.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,149 @@
<template>
  <TableLayout :permissions="['business:member:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="会员信息" prop="keyword">
        <el-input v-model="searchForm.keyword" clearable placeholder="请输入用户昵称/会员姓名/手机号" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="状态" prop="status">
        <el-select v-model="searchForm.status" clearable placeholder="请选择状态" @change="search">
          <el-option label="禁用" :value="0"></el-option>
          <el-option label="启用" :value="1"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="创建时间" prop="createTime">
        <el-date-picker type="daterange" v-model="searchForm.createTime" clearable value-format="yyyy-MM-dd"
                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="handleDateChange" />
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
        <el-button :loading="isWorking.export" @click="handleExport">导出</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="nickName" label="用户昵称" min-width="120px"></el-table-column>
        <el-table-column prop="name" label="会员姓名" min-width="120px"></el-table-column>
        <el-table-column prop="phone" label="授权手机号" min-width="130px"></el-table-column>
        <el-table-column prop="createTime" label="创建时间" min-width="160px"></el-table-column>
        <el-table-column label="状态" min-width="80px">
          <template slot-scope="{row}">
            <el-switch
              @change="handleStatusChange($event, row)"
              v-model="row.status"
              active-color="#13ce66"
              inactive-color="#ff4949"
              :active-value="1"
              :inactive-value="0"
            ></el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="100" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleDetail(row)">详情</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
    <OperaMemberDetail ref="operaMemberDetail" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaMemberDetail from '@/components/business/OperaMemberDetail'
import { fetchList, updateStatus, exportExcel } from '@/api/business/memberManage'
export default {
  name: 'MemberManage',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaMemberDetail },
  data () {
    return {
      searchForm: {
        keyword: '',
        status: '',
        createTime: '',
        createStartTime: '',
        createEndTime: ''
      }
    }
  },
  created () {
    this.config({
      api: '/business/member',
      'field.id': 'id'
    })
    this.search()
  },
  methods: {
    loadTableData (resolve) {
      this.isWorking.search = true
      fetchList(this.getTableParams())
        .then(data => {
          this.tableData.list = data.list || []
          this.tableData.pagination.total = data.total || 0
          if (resolve) resolve(data)
        })
        .catch(e => {
          this.$tip.apiFailed(e, '加载失败')
        })
        .finally(() => {
          this.isWorking.search = false
        })
    },
    handleDateChange (val) {
      this.searchForm.createStartTime = val ? val[0] : ''
      this.searchForm.createEndTime = val ? val[1] : ''
      this.search()
    },
    reset () {
      this.searchForm = {
        keyword: '',
        status: '',
        createTime: '',
        createStartTime: '',
        createEndTime: ''
      }
      this.search()
    },
    handleDetail (row) {
      this.$refs.operaMemberDetail.open('会员详情', row)
    },
    handleStatusChange (val, row) {
      updateStatus({ id: row.id, status: val }).then(res => {
        this.$tip.apiSuccess(res || '修改成功')
      }).catch(e => {
        row.status = val === 1 ? 0 : 1
        this.$tip.apiFailed(e)
      })
    },
    handleExport () {
      this.isWorking.export = true
      exportExcel(this.getTableParams())
        .then(res => {
          this.download(res)
          this.$tip.apiSuccess('导出成功')
        })
        .catch(e => {
          this.$tip.apiFailed(e)
        })
        .finally(() => {
          this.isWorking.export = false
        })
    }
  }
}
</script>
admin/src/views/business/shopQualificationList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,155 @@
<template>
  <TableLayout :permissions="['business:shopInfo:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="门店名称" prop="name">
        <el-input v-model="searchForm.name" clearable placeholder="请输入门店名称" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="类型" prop="companyType">
        <el-select v-model="searchForm.companyType" clearable placeholder="请选择类型" @change="search">
          <el-option label="企业" :value="1"></el-option>
          <el-option label="个人" :value="0"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="门店地址" prop="address">
        <el-input v-model="searchForm.address" clearable placeholder="请输入门店地址" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="联系人" prop="linkName">
        <el-input v-model="searchForm.linkName" clearable placeholder="请输入联系人" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="联系电话" prop="linkPhone">
        <el-input v-model="searchForm.linkPhone" clearable placeholder="请输入联系电话" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="提交时间" prop="createTime">
        <el-date-picker type="daterange" v-model="searchForm.createTime" clearable value-format="yyyy-MM-dd"
                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="handleDateChange" />
      </el-form-item>
      <el-form-item label="状态" prop="auditStatus">
        <el-select v-model="searchForm.auditStatus" clearable placeholder="请选择状态" @change="search">
          <el-option label="待审批" :value="0"></el-option>
          <el-option label="审批通过" :value="1"></el-option>
          <el-option label="审批未通过" :value="2"></el-option>
        </el-select>
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <el-table
          :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
        @selection-change="handleSelectionChange"
      >
        <el-table-column type="selection" width="55"></el-table-column>
        <el-table-column prop="name" label="门店名称" min-width="120px">
          <template slot-scope="{row}">
            <span class="link-name" @click="openShopInfo(row)">{{ row.name }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="companyType" label="类型" min-width="80px">
          <template slot-scope="{row}">
            {{row.companyType == 1 ? '企业' : '个人'}}
          </template>
        </el-table-column>
        <el-table-column prop="address" label="门店地址" min-width="200px"></el-table-column>
        <el-table-column prop="linkName" label="联系人" min-width="100px"></el-table-column>
        <el-table-column prop="linkPhone" label="联系电话" min-width="120px"></el-table-column>
        <el-table-column prop="createTime" label="提交日期" min-width="160px"></el-table-column>
        <el-table-column label="审批状态" min-width="100px">
          <template slot-scope="{row}">
            <span style="color: yellow;" v-if="row.auditStatus == 0">待审批</span>
            <span style="color: #13ce66;" v-else-if="row.auditStatus == 1">审批通过</span>
            <span style="color: #ff4949;" v-else-if="row.auditStatus == 2">审批未通过</span>
            <span style="color: #13ce66;" v-else-if="row.auditStatus == 3">已支付押金</span>
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="150" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" v-if="row.auditStatus == 0" @click="handleAudit(row)">审核</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      >
      </pagination>
    </template>
    <!-- é—¨åº—详情 -->
    <OperaShopInfoSeeWindow ref="operaShopInfoSeeWindow" />
    <OperaShopApprovalWindow ref="operaShopApprovalWindow" @success="search" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaShopInfoSeeWindow from '@/components/business/OperaShopInfoSeeWindow'
import OperaShopApprovalWindow from '@/components/business/OperaShopApprovalWindow'
export default {
  name: 'ShopQualificationList',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaShopInfoSeeWindow, OperaShopApprovalWindow },
  data () {
    return {
      searchForm: {
        name: '',
        companyType: '',
        address: '',
        linkName: '',
        linkPhone: '',
        createTime: '',
        createStartTime: '',
        createEndTime: '',
        auditStatusList: [0,1,2]
      }
    }
  },
  created () {
    this.config({
      module: '门店列表',
      api: '/business/shopInfo',
      'field.id': 'id',
      'field.main': 'id'
    })
    this.search()
  },
  methods: {
    reset () {
      this.searchForm = {
        name: '',
        companyType: '',
        address: '',
        linkName: '',
        linkPhone: '',
        createTime: '',
        status: ''
      }
      this.search()
    },
    handleDateChange (val) {
      this.searchForm.createStartTime = val ? val[0] : ''
      this.searchForm.createEndTime = val ? val[1] : ''
    },
    handleAudit (row) {
      this.$refs.operaShopApprovalWindow.open('门店审批', row)
    },
    openShopInfo (row) {
      this.$refs.operaShopInfoSeeWindow.open('门店信息', row)
    }
  }
}
</script>
<style scoped>
.link-name {
  color: #2E68EC;
  text-decoration: underline;
  cursor: pointer;
}
</style>
admin/src/views/business/shopWithdrawList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,156 @@
<template>
  <TableLayout :permissions="['business:withdrawalorders:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="门店名称" prop="shopName">
        <el-input v-model="searchForm.shopName" clearable placeholder="请输入门店名称" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <el-form-item label="申请时间" prop="createTime1">
        <el-date-picker type="daterange" v-model="searchForm.createTime1" clearable value-format="yyyy-MM-dd"
                        range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="handleDateChange" />
      </el-form-item>
      <el-form-item label="审批状态" prop="status">
        <el-select v-model="searchForm.status" clearable placeholder="请选择状态" @change="search">
          <el-option label="提现申请中" :value="0"></el-option>
          <el-option label="提现成功" :value="1"></el-option>
          <el-option label="提现失败" :value="2"></el-option>
        </el-select>
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <div class="total-amount">
        <span>累计提现:¥{{ totalAmount }}</span>
      </div>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="shopName" label="门店名称" min-width="120px"></el-table-column>
        <el-table-column prop="linkName" label="门店联系人" min-width="100px"></el-table-column>
        <el-table-column prop="amount" label="提现金额(元)" min-width="120px">
          <template slot-scope="{row}">
            <span class="amount">Â¥{{ row.amount / 100 }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="createTime" label="申请时间" min-width="160px"></el-table-column>
        <el-table-column label="审核状态" min-width="100px">
          <template slot-scope="{row}">
            <span :style="{ color: getStatusColor(row.status) }">
              {{ getStatusText(row.status) }}
            </span>
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="100" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleView(row)">查看</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
    <OperaWithdrawDetailWindow ref="operaWithdrawDetailWindow" @success="search" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaWithdrawDetailWindow from '@/components/business/OperaWithdrawDetailWindow'
import { fetchList, getTotalAmount } from '@/api/business/shopWithdraw'
export default {
  name: 'ShopWithdrawList',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaWithdrawDetailWindow },
  data () {
    return {
      totalAmount: '0.00',
      searchForm: {
        shopName: '',
        createTime1: '',
        createStartTime: '',
        createEndTime: '',
        status: ''
      }
    }
  },
  created () {
    this.config({
      api: '/business/shopWithdraw',
      'field.id': 'id'
    })
    this.loadTotalAmount()
    this.search()
  },
  methods: {
    loadTotalAmount () {
      getTotalAmount({
        capacity: 99999,
        pageNum: 1,
        model: {
          shopName: this.searchForm.shopName,
          createStartTime: this.searchForm.createStartTime,
          createEndTime: this.searchForm.createEndTime,
          status: this.searchForm.status
        }
      }).then(res => {
        this.totalAmount = res / 100 || '0.00'
      }).catch(e => {
        this.$tip.apiFailed(e)
      })
    },
    getStatusText (status) {
      const map = { 0: '提现申请中', 1: '提现成功', 2: '提现失败' }
      return map[status] || '-'
    },
    getStatusColor (status) {
      const map = { 0: '#E6A23C', 1: '#67C23A', 2: '#F56C6C' }
      return map[status] || '#606266'
    },
    handleDateChange (val) {
      this.searchForm.createStartTime = val ? val[0] : ''
      this.searchForm.createEndTime = val ? val[1] : ''
      this.search()
    },
    reset () {
      this.searchForm = {
        shopName: '',
        createTime1: '',
        createStartTime: '',
        createEndTime: '',
        auditStatus: ''
      }
      this.search()
    },
    handleView (row) {
      this.$refs.operaWithdrawDetailWindow.open('提现详情', row)
    }
  }
}
</script>
<style scoped>
.total-amount {
  padding: 15px 20px;
  background: #f5f7fa;
  border-radius: 8px;
  margin-bottom: 15px;
  font-size: 16px;
  font-weight: bold;
  color: #303133;
}
.amount {
  color: #f56c6c;
  font-weight: bold;
}
</style>
admin/src/views/business/storeList.vue
@@ -108,7 +108,7 @@
        createTime: '',
        createStartTime: '',
        createEndTime: '',
        auditStatus: ''
        auditStatus: 3
      }
    }
  },
@@ -130,7 +130,8 @@
        linkName: '',
        linkPhone: '',
        createTime: '',
        status: ''
        status: '',
        auditStatus: 3
      }
      this.search()
    },
admin/src/views/business/sysParams.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,162 @@
<template>
  <TableLayout>
    <template v-slot:table-wrap>
      <div class="params-container">
      <div class="section">
        <h3 class="section-title">司机规则</h3>
        <div class="form-item">
          <span class="label">司机每日可取消订单次数为</span>
          <el-input-number v-model="form.driverDailyCancelLimit" :min="0" controls-position="right"></el-input-number>
          <span class="desc">次,超过次数后,今日不可抢单</span>
        </div>
        <div class="form-item">
          <span class="label">司机最大同时进行中订单数量为</span>
          <el-input-number v-model="form.orderAcceptLimit" :min="0" controls-position="right"></el-input-number>
          <span class="desc">个,超过该数量不允许抢单<span class="red">(即订单状态=待取件/配送中)</span></span>
        </div>
      </div>
      <div class="section">
        <h3 class="section-title">订单规则</h3>
        <div class="form-item">
          <span class="label">超时</span>
          <el-input-number v-model="form.autoCancelTime" :min="0" controls-position="right"></el-input-number>
          <span class="desc">分钟未支付,订单自动取消</span>
        </div>
        <div class="form-item">
          <span class="label">急速达订单计价系数</span>
          <el-input-number v-model="form.urgentCoefficient" :min="0" :precision="2" controls-position="right"></el-input-number>
        </div>
        <div class="form-item">
          <span class="label">物品保费比例</span>
          <el-input-number v-model="form.insuranceRate" :min="0" :precision="2" controls-position="right"></el-input-number>
          <span class="desc">%</span>
        </div>
        <div class="form-item">
          <span class="label">异地寄送模式下,若物品已送达,而客户当天未取件,则后续系统按</span>
          <el-input-number v-model="form.unpickedDiscount" :min="0" :precision="2" controls-position="right"></el-input-number>
          <span class="desc">折扣系数计算</span>
        </div>
        <div class="form-item">
          <span class="label">订单送达后默认</span>
          <el-input-number v-model="form.autoConfirmReceipt" :min="0" controls-position="right"></el-input-number>
          <span class="desc">天后自动确认收货<span class="red">(仅限收件地址不是服务点的情况)</span></span>
        </div>
        <div class="form-item">
          <span class="label">订单完成后</span>
          <el-input-number v-model="form.settlementDate" :min="0" controls-position="right"></el-input-number>
          <span class="desc">天后系统结算订单,并给与参与角色按比例分成</span>
        </div>
      </div>
      <div class="footer">
        <el-button type="primary" @click="handleSave" :loading="isWorking.save">保存</el-button>
      </div>
    </div>
    </template>
  </TableLayout>
</template>
<script>
import TableLayout from '@/layouts/TableLayout'
import { getParams, saveParams } from '@/api/business/sysParams'
export default {
  name: 'SysParams',
  components: { TableLayout },
  data () {
    return {
      form: {
        autoCancelTime: '',
        autoConfirmReceipt: '',
        driverDailyCancelLimit: '',
        insuranceRate: '',
        orderAcceptLimit: '',
        settlementDate: '',
        unpickedDiscount: '',
        urgentCoefficient: ''
      },
      isWorking: {
        save: false
      }
    }
  },
  created () {
    this.loadData()
  },
  methods: {
    loadData () {
      getParams().then(res => {
        this.form = {
          autoCancelTime: res.autoCancelTime || '',
          autoConfirmReceipt: res.autoConfirmReceipt || '',
          driverDailyCancelLimit: res.driverDailyCancelLimit || '',
          insuranceRate: res.insuranceRate || '',
          orderAcceptLimit: res.orderAcceptLimit || '',
          settlementDate: res.settlementDate || '',
          unpickedDiscount: res.unpickedDiscount || '',
          urgentCoefficient: res.urgentCoefficient || ''
        }
      }).catch(e => {
        this.$tip.apiFailed(e)
      })
    },
    handleSave () {
      this.isWorking.save = true
      saveParams(this.form).then(res => {
        this.$tip.apiSuccess(res || '保存成功')
      }).catch(e => {
        this.$tip.apiFailed(e)
      }).finally(() => {
        this.isWorking.save = false
      })
    }
  }
}
</script>
<style scoped>
.params-container {
  padding: 20px;
}
.section {
  margin-bottom: 40px;
}
.section-title {
  font-size: 16px;
  font-weight: bold;
  color: #303133;
  margin-bottom: 20px;
  padding-left: 10px;
  border-left: 4px solid #2E68EC;
}
.form-item {
  display: flex;
  align-items: center;
  margin-bottom: 20px;
  flex-wrap: wrap;
}
.form-item .label {
  color: #606266;
  font-size: 14px;
  white-space: nowrap;
}
.form-item .desc {
  color: #909399;
  font-size: 14px;
  margin-left: 10px;
}
.form-item .red {
  color: #f56c6c;
}
.form-item /deep/ .el-input-number {
  margin: 0 10px;
}
.form-item /deep/ .el-input-number .el-input__inner {
  text-align: center;
}
.footer {
  padding-top: 20px;
  border-top: 1px solid #eee;
}
</style>
admin/src/views/business/vehicleType.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,99 @@
<template>
  <TableLayout :permissions="['business:category:query']">
    <el-form ref="searchForm" slot="search-form" :model="searchForm" label-width="100px" inline>
      <el-form-item label="名称" prop="name">
        <el-input v-model="searchForm.name" clearable placeholder="请输入名称" @keypress.enter.native="search"></el-input>
      </el-form-item>
      <section>
        <el-button type="primary" @click="search">搜索</el-button>
        <el-button @click="reset">重置</el-button>
      </section>
    </el-form>
    <template v-slot:table-wrap>
      <ul class="toolbar">
        <li><el-button type="primary" @click="$refs.operaGoodsCategoryEditWindow.open('新建物品等级', null, 1)" icon="el-icon-plus">新建</el-button></li>
      </ul>
      <el-table
        :height="tableHeightNew"
        v-loading="isWorking.search"
        :data="tableData.list"
        stripe
      >
        <el-table-column prop="id" label="ID" min-width="80px"></el-table-column>
        <el-table-column prop="name" label="类型名称" min-width="120px"></el-table-column>
        <el-table-column prop="detail" label="通行方式" min-width="200px">
          <template slot-scope="{row}">
            {{ row.otherField === 'driving' ? '机动车' : '非机动车' }}
          </template>
        </el-table-column>
        <el-table-column prop="sortnum" label="分类排序" min-width="100px"></el-table-column>
        <el-table-column label="状态" min-width="80px">
          <template slot-scope="{row}">
            <el-switch
              @change="handleStatusChange($event, row)"
              v-model="row.status"
              active-color="#13ce66"
              inactive-color="#ff4949"
              :active-value="0"
              :inactive-value="1"
            ></el-switch>
          </template>
        </el-table-column>
        <el-table-column label="操作" min-width="150" fixed="right">
          <template slot-scope="{row}">
            <el-button type="text" @click="handleEdit(row)">编辑</el-button>
            <el-button type="text" @click="deleteById(row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>
      <pagination
        @size-change="handleSizeChange"
        @current-change="handlePageChange"
        :pagination="tableData.pagination"
      ></pagination>
    </template>
    <OperaGoodsCategoryEditWindow ref="operaGoodsCategoryEditWindow" @success="search" />
  </TableLayout>
</template>
<script>
import BaseTable from '@/components/base/BaseTable'
import TableLayout from '@/layouts/TableLayout'
import Pagination from '@/components/common/Pagination'
import OperaGoodsCategoryEditWindow from '@/components/business/OperaGoodsCategoryEditWindow'
import { updateStatus } from '@/api/business/goodsCategory'
export default {
  name: 'VehicleType',
  extends: BaseTable,
  components: { TableLayout, Pagination, OperaGoodsCategoryEditWindow },
  data () {
    return {
      searchForm: {
        name: '',
        type: 1
      }
    }
  },
  created () {
    this.config({
      api: '/business/goodsCategory',
      'field.id': 'id'
    })
    this.search()
  },
  methods: {
    handleEdit (row) {
      this.$refs.operaGoodsCategoryEditWindow.open('编辑物品等级', row, 1)
    },
    handleStatusChange (val, row) {
      updateStatus({ id: row.id, status: val }).then(res => {
        this.$tip.apiSuccess(res || '修改成功')
      }).catch(e => {
        row.status = val === 1 ? 0 : 1
        this.$tip.apiFailed(e)
      })
    }
  }
}
</script>