| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615 |
- <style scoped lang="scss">
- .device-order-container {
- padding: 10px 0;
- }
- .device-order-list {
- max-height: 400px;
- overflow-y: auto;
- }
- .device-order-item {
- display: flex;
- align-items: center;
- padding: 12px 16px;
- margin-bottom: 8px;
- background: #fafafa;
- border: 1px solid #ebeef5;
- border-radius: 6px;
- cursor: grab;
- transition: all 0.2s;
- user-select: none;
- &:hover {
- background: #f0f2f5;
- border-color: #d9dce1;
- }
- &.dragging {
- opacity: 0.5;
- background: #e6f7ff;
- border-color: #409eff;
- }
- &.drag-over {
- border-color: #409eff;
- background: #ecf5ff;
- }
- }
- .drag-handle {
- margin-right: 12px;
- cursor: grab;
- color: #c0c4cc;
- font-size: 18px;
- display: flex;
- align-items: center;
- &:active {
- cursor: grabbing;
- }
- }
- .order-sequence {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 32px;
- height: 32px;
- background: #409eff;
- color: #fff;
- border-radius: 50%;
- font-size: 14px;
- font-weight: 600;
- margin-right: 16px;
- flex-shrink: 0;
- }
- .order-device-info {
- flex: 1;
- min-width: 0;
- .order-device-name {
- font-size: 14px;
- font-weight: 500;
- color: #303133;
- }
- .order-device-id {
- font-size: 12px;
- color: #909399;
- margin-top: 2px;
- }
- }
- .device-order-footer {
- margin-top: 20px;
- padding-top: 16px;
- border-top: 1px solid #ebeef5;
- text-align: right;
- }
- </style>
- <template>
- <div class="system-dialog-container">
- <el-dialog
- :title="state.dialog.title"
- v-model="state.dialog.isShowDialog"
- width="720px"
- draggable
- destroy-on-close
- :close-on-click-modal="false"
- align-center>
- <el-tabs v-model="state.tab" class="demo-tabs" style="width: 100%;" @tab-change="handleTabChange">
- <el-tab-pane label="基本信息" name="basic">
- <el-form
- inline
- :model="state.ruleForm"
- :rules="state.rules"
- label-position="top"
- ref="formRef"
- size="default"
- label-width="100px"
- class="mt5">
- <el-form-item label="站点ID:" prop="stationId" class="wd350">
- <el-input
- v-model="state.ruleForm.stationId"
- placeholder="站点ID"
- clearable
- class="wd100">
- </el-input>
- </el-form-item>
- <el-form-item label="工位数量:" prop="parkingNum">
- <el-input
- v-model="state.ruleForm.parkingNum"
- placeholder="工位数量"
- clearable
- class="wd100">
- </el-input>
- </el-form-item>
- <el-form-item label="站点名称:" prop="stationName">
- <el-input
- v-model="state.ruleForm.stationName"
- placeholder="站点名称"
- clearable
- class="wd200">
- </el-input>
- </el-form-item>
- <el-form-item label="站点照片:" prop="pictures">
- <ext-upload v-model="state.ruleForm.pictures" multiple max="6"></ext-upload>
- <!-- <el-input
- v-model="state.ruleForm.pictures"
- placeholder="站点照片"
- clearable
- class="wd350">
- </el-input>-->
- </el-form-item>
- <el-form-item class="w100"></el-form-item>
- <el-form-item label="站点状态:" prop="stationStatus">
- <ext-d-select type="WashStation.status" class="wd200" v-model="state.ruleForm.stationStatus"></ext-d-select>
- </el-form-item>
- <el-form-item label="站点类型:" prop="stationType">
- <ext-d-select type="WashStation.type" class="wd200" v-model="state.ruleForm.stationType"></ext-d-select>
- </el-form-item>
- <el-form-item label="运营主体:" prop="operationEntity" class="wd350">
- <el-input
- v-model="state.ruleForm.operationEntity"
- placeholder="运营主体"
- clearable
- class="wd100">
- </el-input>
- </el-form-item>
- <el-form-item label="统一社会信用代码:" prop="creditCode" class="wd350">
- <el-input
- v-model="state.ruleForm.creditCode"
- placeholder="统一社会信用代码"
- clearable
- class="wd100">
- </el-input>
- </el-form-item>
- <el-form-item label="场站联系人:" prop="contactPerson" class="wd350">
- <el-input
- v-model="state.ruleForm.contactPerson"
- placeholder="场站联系人"
- clearable
- class="wd100">
- </el-input>
- </el-form-item>
- <el-form-item label="地址:" prop="address" class="w100">
- <el-input
- v-model="state.ruleForm.address"
- placeholder="地址"
- clearable
- class="w100">
- </el-input>
- </el-form-item>
- <el-form-item label="经度坐标:" prop="location">
- <el-input
- v-model="state.location.stationLng"
- placeholder="经度坐标"
- clearable
- type="number"
- class="wd200">
- </el-input>
- </el-form-item>
- <el-form-item label="纬度坐标:" prop="location">
- <el-input
- v-model="state.location.stationLat"
- placeholder="纬度坐标"
- clearable
- type="number"
- class="wd200">
- </el-input>
- </el-form-item>
- <el-form-item label="服务电话:" prop="serviceTel">
- <el-input
- v-model="state.ruleForm.serviceTel"
- placeholder="服务电话"
- clearable
- class="wd350">
- </el-input>
- </el-form-item>
- <el-form-item label="站点电话:" prop="stationTel">
- <el-input
- v-model="state.ruleForm.stationTel"
- placeholder="站点电话"
- clearable
- class="wd350">
- </el-input>
- </el-form-item>
- <el-form-item label="营业时间描述:" prop="businessHours" class="w100">
- <el-input
- v-model="state.ruleForm.businessHours"
- placeholder="营业时间描述"
- clearable
- type="textarea"
- class="w100">
- </el-input>
- </el-form-item>
- <el-form-item label="停车费描述:" prop="parkingFee" class="w100">
- <el-input
- v-model="state.ruleForm.parkingFee"
- type="textarea"
- placeholder="停车费描述:eg:洗车费用满10元减免1小时停车费"
- clearable
- class="w100">
- </el-input>
- </el-form-item>
- <el-form-item label="停车费减免二维码文本:" prop="parkingQrCode" class="w100">
- <el-input
- v-model="state.ruleForm.parkingQrCode"
- type="textarea"
- placeholder="停车费减免二维码文本"
- clearable
- class="w100">
- </el-input>
- </el-form-item>
- <el-form-item label="备注:" prop="remark" class="w100">
- <el-input
- v-model="state.ruleForm.remark"
- placeholder="备注"
- clearable
- type="textarea"
- class="w100">
- </el-input>
- </el-form-item>
- </el-form>
- </el-tab-pane>
- <el-tab-pane label="费率信息" name="fee">
- <el-form
- inline
- :model="state.feeForm"
- :rules="state.feeRules"
- label-position="top"
- ref="feeFormRef"
- size="default"
- label-width="100px"
- class="mt5">
- <el-form-item label="站点" prop="feeRate" class="wd250">
- <ext-select
- v-model="state.feeForm.stationId"
- placeholder="站点"
- url="washStation/list"
- url-method="post"
- label-key="stationName"
- value-key="stationId"
- data-key="list"
- clearable
- disabled
- class="wd200 ml10"/>
- </el-form-item>
- <el-form-item label="绑定平台费率" prop="feeRate" class="wd250">
- <ext-select
- v-model="state.feeForm.feeRateId"
- placeholder="平台费率配置"
- clearable
- :dataList="state.platformFeeRateList"
- @on-change="handlePlatformFeeRateChange"
- class="wd200 ml10"/>
- </el-form-item>
- <el-form-item label="平台费率(0.1代表10%)" prop="feeRate" class="wd250">
- <el-input-number
- v-model="state.feeForm.feeRate"
- placeholder="平台费率(0.1代表10%)"
- clearable
- step="0.1"
- class="w100">
- </el-input-number>
- </el-form-item>
- <el-form-item label="充值冻结金额比例(0.3代表30%)" prop="frozenRatio" class="wd250">
- <el-input-number
- v-model="state.feeForm.frozenRatio"
- placeholder="充值冻结金额比例(0.3代表30%)"
- clearable
- step="0.1"
- class="w100">
- </el-input-number>
- </el-form-item>
- <el-form-item label="提现手续费率(0.006代表6‰)" prop="withdrawalFeeRate" class="wd250">
- <el-input-number
- v-model="state.feeForm.withdrawalFeeRate"
- placeholder="提现手续费率(0.006代表6‰)"
- clearable
- step="0.001"
- class="w100">
- </el-input-number>
- </el-form-item>
- </el-form>
- </el-tab-pane>
- <el-tab-pane label="调整设备顺序" name="deviceOrder" v-if="state.action !== 'add'">
- <div class="device-order-container" v-loading="state.deviceOrderLoading">
- <el-alert
- title="上下拖动设备行可调整工位顺序,调整后请点击下方保存按钮"
- type="info" :closable="false" show-icon style="margin-bottom: 16px;" />
- <div class="device-order-list" v-if="state.deviceOrderList.length > 0">
- <div
- v-for="(device, index) in state.deviceOrderList"
- :key="device.id"
- class="device-order-item"
- :class="{ 'dragging': state.dragIndex === index, 'drag-over': state.dragOverIndex === index }"
- draggable="true"
- @dragstart="handleDragStart($event, index)"
- @dragover.prevent="handleDragOver($event, index)"
- @drop="handleDrop($event, index)"
- @dragend="handleDragEnd"
- >
- <div class="drag-handle">
- <SvgIcon name="ele-Rank"/>
- </div>
- <div class="order-sequence">{{ index + 1 }}</div>
- <div class="order-device-info">
- <div class="order-device-name">{{ device.deviceName }}</div>
- <div class="order-device-id">{{ device.shortId || device.productKey }}</div>
- </div>
- </div>
- </div>
- <el-empty v-else description="该站点暂无设备" />
- <div class="device-order-footer">
- <el-button type="primary" @click="saveDeviceOrder" :loading="state.deviceOrderSaving" :disabled="state.deviceOrderList.length === 0">保存顺序</el-button>
- </div>
- </div>
- </el-tab-pane>
- </el-tabs>
- <!-- <el-form-item label="站点引导" prop="siteGuide">
- <el-input
- v-model="state.ruleForm.siteGuide"
- placeholder="站点引导"
- clearable
- class="wd350">
- </el-input>
- </el-form-item>-->
- <template #footer v-if="state.tab !== 'deviceOrder'">
- <span class="dialog-footer">
- <el-button @click="onCancel" size="default">取 消</el-button>
- <el-button :loading="state.btnLoading" type="primary" @click="onSubmit" size="default">{{ state.dialog.submitTxt }}</el-button>
- </span>
- </template>
- </el-dialog>
- </div>
- </template>
- <script setup lang="ts" name="WashStationDialog">
- import {defineAsyncComponent, reactive, onMounted, ref} from 'vue';
- import {Msg} from "/@/utils/message";
- import {$body, $get} from "/@/utils/request";
- import u from '/@/utils/u'
- import ExtUpload from "/@/components/form/ExtUpload.vue";
- import ExtDSelect from "/@/components/form/ExtDSelect.vue";
- import ExtSelect from "/@/components/form/ExtSelect.vue";
- // 引入组件
- // 定义子组件向父组件传值/事件
- const emit = defineEmits(['refresh']);
- const formRef = ref();
- //定义初始变量,重置使用
- const initState = () => ({
- ruleForm: {
- id: null,
- location: {}
- },
- feeForm: {
- stationId: null as any
- },
- btnLoading: false,
- dialog: {
- isShowDialog: false,
- type: '',
- title: '',
- submitTxt: '',
- },
- rules: {},
- feeRules: {
- },
- location: {},
- tab: 'basic',
- platformFeeRateList: [],
- stationFeeRate: [],
- action:'',
- deviceOrderList: [] as any[],
- deviceOrderLoading: false,
- deviceOrderSaving: false,
- dragIndex: -1,
- dragOverIndex: -1,
- })
- // 定义变量内容
- const state = reactive(initState());
- // 打开弹窗
- const open = (action: string = 'add', row: any) => {
- state.action = action;
- state.dialog.title = u.dialog.actions[action].title + "『站点信息』"
- state.dialog.submitTxt = u.dialog.actions[action].btn + "『站点信息』"
- state.dialog.isShowDialog = true;
- if (action !== 'add') {
- loadData(row.id);
- state.feeForm.stationId = row.stationId
- loadStationFeeRate();
- loadDeviceListForStation(row.stationId);
- } else {
- state.ruleForm = Object.assign(state.ruleForm, row);
- }
- loadPlatformFeeRateList()
- };
- // 关闭弹窗
- const onClose = () => {
- state.dialog.isShowDialog = false;
- Object.assign(state, initState())
- };
- // 取消
- const onCancel = () => {
- onClose();
- };
- // 提交
- const onSubmit = () => {
- if(state.tab==='basic'){
- formRef.value.validate((v: boolean) => {
- if (v) {
- state.btnLoading = true;
- state.ruleForm.location = JSON.stringify(state.location)
- const url = !!state.ruleForm.id ? "washStation/modify" : "washStation/add"
- $body(url, state.ruleForm).then(() => {
- state.btnLoading = false;
- Msg.message('操作成功');
- //console.log('submit!')
- onClose();
- emit('refresh');
- }).catch(e=>{
- console.error(e);
- state.btnLoading = false;
- })
- } else {
- state.btnLoading = false;
- Msg.message('请先完整填写表单', 'error');
- }
- })
- }else{
- state.btnLoading = true;
- $body(`station-fee-rate/bind`,state.feeForm).then(res=>{
- Msg.message('站点费率绑定成功');
- state.btnLoading = false;
- onClose();
- emit('refresh');
- }).catch(e=>{
- Msg.message('操作失败','error');
- state.btnLoading = false;
- })
- }
- };
- const handleFormChange = (formData: any) => {
- //console.log(formData)
- }
- // 初始化数据
- const loadData = (id: number) => {
- $get(`washStation/detail/${id}`).then((res: any) => {
- state.ruleForm = res;
- state.location = JSON.parse(res.location)
- })
- }
- const loadStationFeeRate = () => {
- $get(`station-fee-rate/${state.feeForm.stationId}`).then(res => {
- state.feeForm = res;
- })
- }
- const loadPlatformFeeRateList = () => {
- $body(`platform-fee-rate/list`, {pageSize: 1024}).then(res => {
- state.platformFeeRateList = res.list;
- })
- }
- const loadDeviceListForStation = (stationId?: string) => {
- const sid = stationId || state.ruleForm.stationId
- if (!sid) return
- state.deviceOrderLoading = true
- $body('/washDevice/list', { stationId: sid, pageNum: 1, pageSize: 200 }).then((res: any) => {
- state.deviceOrderList = (res.list || []).map((d: any) => ({ ...d }))
- }).finally(() => {
- state.deviceOrderLoading = false
- })
- }
- const handleDragStart = (e: DragEvent, index: number) => {
- state.dragIndex = index
- if (e.dataTransfer) {
- e.dataTransfer.effectAllowed = 'move'
- }
- }
- const handleDragOver = (_e: DragEvent, index: number) => {
- state.dragOverIndex = index
- }
- const handleDrop = (_e: DragEvent, index: number) => {
- if (state.dragIndex === -1 || state.dragIndex === index) return
- const list = [...state.deviceOrderList]
- const [item] = list.splice(state.dragIndex, 1)
- list.splice(index, 0, item)
- state.deviceOrderList = list
- }
- const handleDragEnd = () => {
- state.dragIndex = -1
- state.dragOverIndex = -1
- }
- const saveDeviceOrder = () => {
- state.deviceOrderSaving = true
- const devices = state.deviceOrderList.map((d: any, idx: number) => ({
- id: d.id,
- sequence: idx + 1
- }))
- $body('/washDevice/batchUpdateSequence', devices).then(() => {
- Msg.message('设备顺序保存成功', 'success')
- loadDeviceListForStation()
- }).catch(() => {
- Msg.message('保存失败', 'error')
- }).finally(() => {
- state.deviceOrderSaving = false
- })
- }
- const handlePlatformFeeRateChange = (platformFeeRateId: any) => {
- console.log(platformFeeRateId)
- let rate = state.platformFeeRateList.find(k => k.id == platformFeeRateId);
- if (rate) {
- let {feeRate, withdrawalFeeRate, frozenRatio} = rate;
- state.feeForm = Object.assign({}, state.feeForm, {feeRate, withdrawalFeeRate, frozenRatio})
- } else {
- state.feeForm.withdrawalFeeRate = 0;
- state.feeForm.feeRate = 0;
- state.feeForm.frozenRatio = 0;
- }
- }
- const handleTabChange = (tab:string) => {
- if(tab==='basic'){
- state.dialog.submitTxt = u.dialog.actions[state.action].btn + "『站点信息』"
- }else if(tab==='fee'){
- state.dialog.submitTxt = u.dialog.actions[state.action].btn + "『站点费率』"
- }else if(tab==='deviceOrder'){
- loadDeviceListForStation()
- }
- }
- // 暴露变量
- defineExpose({
- open
- });
- </script>
|