Commit 24f8cd2dff6a9300dc428bf02e041cbfbb170d4e
Merge branch 'master' of http://39.98.150.180/antissoft/lvqianmeiye_ERP
Showing
1 changed file
with
700 additions
and
240 deletions
antis-ncc-admin/src/views/lqMdxx/Form.vue
| 1 | -<template> | |
| 2 | - <el-dialog :title="!dataForm.id ? '新建' : isDetail ? '详情':'编辑'" :close-on-click-modal="false" :visible.sync="visible" class="NCC-dialog NCC-dialog_center" lock-scroll width="600px"> | |
| 3 | - <el-row :gutter="15" class="" > | |
| 4 | - <el-form ref="elForm" :model="dataForm" size="small" label-width="100px" label-position="right" :disabled="!!isDetail" :rules="rules"> | |
| 5 | - <el-col :span="24" v-if="false" > | |
| 6 | - <el-form-item label="主键" prop="id"> | |
| 7 | - <el-input v-model="dataForm.id" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 8 | - </el-input> | |
| 9 | - </el-form-item> | |
| 10 | - </el-col> | |
| 11 | - <el-col :span="24"> | |
| 12 | - <el-form-item label="门店编码" prop="mdbm"> | |
| 13 | - <el-input v-model="dataForm.mdbm" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 14 | - </el-input> | |
| 15 | - </el-form-item> | |
| 16 | - </el-col> | |
| 17 | - <el-col :span="24"> | |
| 18 | - <el-form-item label="单据门店编号" prop="djmdbh"> | |
| 19 | - <el-input v-model="dataForm.djmdbh" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 20 | - </el-input> | |
| 21 | - </el-form-item> | |
| 22 | - </el-col> | |
| 23 | - <el-col :span="24"> | |
| 24 | - <el-form-item label="单据门店" prop="djmd"> | |
| 25 | - <el-input v-model="dataForm.djmd" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 26 | - </el-input> | |
| 27 | - </el-form-item> | |
| 28 | - </el-col> | |
| 29 | - <el-col :span="24"> | |
| 30 | - <el-form-item label="店名" prop="dm"> | |
| 31 | - <el-input v-model="dataForm.dm" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 32 | - </el-input> | |
| 33 | - </el-form-item> | |
| 34 | - </el-col> | |
| 35 | - <el-col :span="24"> | |
| 36 | - <el-form-item label="城市" prop="cs"> | |
| 37 | - <el-input v-model="dataForm.cs" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 38 | - </el-input> | |
| 39 | - </el-form-item> | |
| 40 | - </el-col> | |
| 41 | - <el-col :span="24"> | |
| 42 | - <el-form-item label="地址" prop="dz"> | |
| 43 | - <el-input v-model="dataForm.dz" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 44 | - </el-input> | |
| 45 | - </el-form-item> | |
| 46 | - </el-col> | |
| 47 | - <el-col :span="24"> | |
| 48 | - <el-form-item label="姓名" prop="xm"> | |
| 49 | - <el-input v-model="dataForm.xm" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 50 | - </el-input> | |
| 51 | - </el-form-item> | |
| 52 | - </el-col> | |
| 53 | - <el-col :span="24"> | |
| 54 | - <el-form-item label="电话号码" prop="dhhm"> | |
| 55 | - <el-input v-model="dataForm.dhhm" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 56 | - </el-input> | |
| 57 | - </el-form-item> | |
| 58 | - </el-col> | |
| 59 | - <el-col :span="24"> | |
| 60 | - <el-form-item label="座机" prop="zj"> | |
| 61 | - <el-input v-model="dataForm.zj" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 62 | - </el-input> | |
| 63 | - </el-form-item> | |
| 64 | - </el-col> | |
| 65 | - <el-col :span="24"> | |
| 66 | - <el-form-item label="开业时间" prop="kysj"> | |
| 67 | - <el-date-picker v-model="dataForm.kysj" placeholder="请选择" clearable :style='{"width":"100%"}' type='date' format="yyyy-MM-dd" value-format="timestamp" > | |
| 68 | - </el-date-picker> | |
| 69 | - </el-form-item> | |
| 70 | - </el-col> | |
| 71 | - <el-col :span="24"> | |
| 72 | - <el-form-item label="最新状态" prop="zxzt"> | |
| 73 | - <el-select v-model="dataForm.zxzt" placeholder="请选择" clearable :style='{"width":"100%"}' > | |
| 74 | - <el-option v-for="(item, index) in zxztOptions" :key="index" :label="item.fullName" :value="item.id" ></el-option> | |
| 75 | - </el-select> | |
| 76 | - </el-form-item> | |
| 77 | - </el-col> | |
| 78 | - <el-col :span="24"> | |
| 79 | - <el-form-item label="工商名称" prop="gsmc"> | |
| 80 | - <el-input v-model="dataForm.gsmc" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 81 | - </el-input> | |
| 82 | - </el-form-item> | |
| 83 | - </el-col> | |
| 84 | - <el-col :span="24"> | |
| 85 | - <el-form-item label="法人" prop="fr"> | |
| 86 | - <el-input v-model="dataForm.fr" placeholder="请输入" clearable :style='{"width":"100%"}' > | |
| 87 | - </el-input> | |
| 88 | - </el-form-item> | |
| 89 | - </el-col> | |
| 90 | - <el-col :span="24"> | |
| 91 | - <el-form-item label="有无社保" prop="ywsb"> | |
| 92 | - <el-select v-model="dataForm.ywsb" placeholder="请选择" clearable :style='{"width":"100%"}' > | |
| 93 | - <el-option v-for="(item, index) in ywsbOptions" :key="index" :label="item.fullName" :value="item.id" ></el-option> | |
| 94 | - </el-select> | |
| 95 | - </el-form-item> | |
| 96 | - </el-col> | |
| 97 | - <el-col :span="24"> | |
| 98 | - <el-form-item label="在职人数" prop="zzrs"> | |
| 99 | - <el-input-number v-model="dataForm.zzrs" :min="0" :step="1" :precision="0" placeholder="请输入" :style='{"width":"100%"}' /> | |
| 100 | - </el-form-item> | |
| 101 | - </el-col> | |
| 102 | - <el-col :span="24"> | |
| 103 | - <el-form-item label="门店类别" prop="storeCategory"> | |
| 104 | - <el-select v-model="dataForm.storeCategory" placeholder="请选择" clearable :style='{"width":"100%"}' > | |
| 105 | - <el-option v-for="(item, index) in storeCategoryOptions" :key="index" :label="item.Name" :value="item.Value" ></el-option> | |
| 106 | - </el-select> | |
| 107 | - </el-form-item> | |
| 108 | - </el-col> | |
| 109 | - <el-col :span="24"> | |
| 110 | - <el-form-item label="门店类型" prop="storeType"> | |
| 111 | - <el-select v-model="dataForm.storeType" placeholder="请选择" clearable :style='{"width":"100%"}' > | |
| 112 | - <el-option v-for="(item, index) in storeTypeOptions" :key="index" :label="item.Name" :value="item.Value" ></el-option> | |
| 113 | - </el-select> | |
| 114 | - </el-form-item> | |
| 115 | - </el-col> | |
| 116 | - </el-form> | |
| 1 | +<template> | |
| 2 | + <el-dialog :title="!dataForm.id ? '新建' : isDetail ? '详情' : '编辑'" :close-on-click-modal="false" | |
| 3 | + :visible.sync="visible" class="NCC-dialog NCC-dialog_center" lock-scroll width="720px"> | |
| 4 | + <el-row :gutter="15"> | |
| 5 | + <el-form ref="elForm" :model="dataForm" size="small" label-width="100px" label-position="right" | |
| 6 | + :disabled="!!isDetail" :rules="rules"> | |
| 7 | + <!-- 基础信息字段 --> | |
| 8 | + <el-col :span="24" v-if="false"> | |
| 9 | + <el-form-item label="主键" prop="id"> | |
| 10 | + <el-input v-model="dataForm.id" /> | |
| 11 | + </el-form-item> | |
| 12 | + </el-col> | |
| 13 | + <el-col :span="12"> | |
| 14 | + <el-form-item label="门店编码" prop="mdbm"> | |
| 15 | + <el-input v-model="dataForm.mdbm" placeholder="请输入" clearable /> | |
| 16 | + </el-form-item> | |
| 17 | + </el-col> | |
| 18 | + <el-col :span="12"> | |
| 19 | + <el-form-item label="店名" prop="dm"> | |
| 20 | + <el-input v-model="dataForm.dm" placeholder="请输入" clearable /> | |
| 21 | + </el-form-item> | |
| 22 | + </el-col> | |
| 23 | + <el-col :span="24"> | |
| 24 | + <el-form-item label="地址" prop="dz"> | |
| 25 | + <el-input v-model="dataForm.dz" placeholder="请输入完整地址" clearable> | |
| 26 | + <el-button slot="append" icon="el-icon-location-outline" | |
| 27 | + @click="handleOpenLocation">地图定位</el-button> | |
| 28 | + </el-input> | |
| 29 | + </el-form-item> | |
| 30 | + </el-col> | |
| 31 | + <el-col :span="12"> | |
| 32 | + <el-form-item label="经度" prop="longitude"> | |
| 33 | + <el-input v-model="dataForm.longitude" readonly placeholder="请通过地图定位选择" /> | |
| 34 | + </el-form-item> | |
| 35 | + </el-col> | |
| 36 | + <el-col :span="12"> | |
| 37 | + <el-form-item label="纬度" prop="latitude"> | |
| 38 | + <el-input v-model="dataForm.latitude" readonly placeholder="请通过地图定位选择" /> | |
| 39 | + </el-form-item> | |
| 40 | + </el-col> | |
| 41 | + <el-col :span="24"> | |
| 42 | + <el-form-item label="电子围栏"> | |
| 43 | + <div class="fence-status-bar"> | |
| 44 | + <el-tag :type="dataForm.fencePolygons && dataForm.fencePolygons.length ? 'success' : 'info'" | |
| 45 | + size="medium"> | |
| 46 | + {{ (dataForm.fencePolygons && dataForm.fencePolygons.length) ? '已设置围栏 (1块)' : '未设置围栏' }} | |
| 47 | + </el-tag> | |
| 48 | + <el-button type="primary" size="mini" icon="el-icon-edit" style="margin-left: 10px" | |
| 49 | + :disabled="!dataForm.longitude || !dataForm.latitude" @click="handleOpenFence"> | |
| 50 | + 设置围栏 | |
| 51 | + </el-button> | |
| 52 | + <span v-if="!dataForm.longitude" class="hint-text">(请先完成地图定位)</span> | |
| 53 | + </div> | |
| 54 | + </el-form-item> | |
| 55 | + </el-col> | |
| 56 | + | |
| 57 | + <!-- 其他业务字段 --> | |
| 58 | + <el-col :span="12"> | |
| 59 | + <el-form-item label="城市" prop="cs"> | |
| 60 | + <el-input v-model="dataForm.cs" placeholder="请输入" clearable /> | |
| 61 | + </el-form-item> | |
| 62 | + </el-col> | |
| 63 | + <el-col :span="12"> | |
| 64 | + <el-form-item label="最新状态" prop="zxzt"> | |
| 65 | + <el-select v-model="dataForm.zxzt" placeholder="请选择" clearable :style='{ "width": "100%" }'> | |
| 66 | + <el-option v-for="(item, index) in zxztOptions" :key="index" :label="item.fullName" | |
| 67 | + :value="item.id"></el-option> | |
| 68 | + </el-select> | |
| 69 | + </el-form-item> | |
| 70 | + </el-col> | |
| 71 | + <el-col :span="12"> | |
| 72 | + <el-form-item label="门店类别" prop="storeCategory"> | |
| 73 | + <el-select v-model="dataForm.storeCategory" placeholder="请选择" clearable | |
| 74 | + :style='{ "width": "100%" }'> | |
| 75 | + <el-option v-for="(item, index) in storeCategoryOptions" :key="index" :label="item.Name" | |
| 76 | + :value="item.Value"></el-option> | |
| 77 | + </el-select> | |
| 78 | + </el-form-item> | |
| 79 | + </el-col> | |
| 80 | + <el-col :span="12"> | |
| 81 | + <el-form-item label="门店类型" prop="storeType"> | |
| 82 | + <el-select v-model="dataForm.storeType" placeholder="请选择" clearable | |
| 83 | + :style='{ "width": "100%" }'> | |
| 84 | + <el-option v-for="(item, index) in storeTypeOptions" :key="index" :label="item.Name" | |
| 85 | + :value="item.Value"></el-option> | |
| 86 | + </el-select> | |
| 87 | + </el-form-item> | |
| 88 | + </el-col> | |
| 89 | + <el-col :span="12"> | |
| 90 | + <el-form-item label="姓名" prop="xm"> | |
| 91 | + <el-input v-model="dataForm.xm" placeholder="请输入" clearable /> | |
| 92 | + </el-form-item> | |
| 93 | + </el-col> | |
| 94 | + <el-col :span="12"> | |
| 95 | + <el-form-item label="电话号码" prop="dhhm"> | |
| 96 | + <el-input v-model="dataForm.dhhm" placeholder="请输入" clearable /> | |
| 97 | + </el-form-item> | |
| 98 | + </el-col> | |
| 99 | + </el-form> | |
| 117 | 100 | </el-row> |
| 118 | 101 | <span slot="footer" class="dialog-footer"> |
| 119 | 102 | <el-button @click="visible = false">取 消</el-button> |
| 120 | 103 | <el-button type="primary" @click="dataFormSubmit()" v-if="!isDetail">确 定</el-button> |
| 121 | 104 | </span> |
| 105 | + | |
| 106 | + <!-- 定位弹窗 --> | |
| 107 | + <el-dialog title="门店地图定位" :visible.sync="locationVisible" width="800px" append-to-body class="map-dialog"> | |
| 108 | + <div class="map-container-wrapper"> | |
| 109 | + <div class="map-header-bar"> | |
| 110 | + <span>在地图上点击以选择门店位置(仅支持单个标记)</span> | |
| 111 | + <div class="coordinate-info" v-if="tempMarker.lng"> | |
| 112 | + 当前选择:{{ tempMarker.lng.toFixed(6) }}, {{ tempMarker.lat.toFixed(6) }} | |
| 113 | + </div> | |
| 114 | + </div> | |
| 115 | + <div id="location-map" class="map-canvas"></div> | |
| 116 | + </div> | |
| 117 | + <span slot="footer" class="dialog-footer"> | |
| 118 | + <el-button @click="locationVisible = false">取 消</el-button> | |
| 119 | + <el-button type="primary" @click="confirmLocation" :disabled="!tempMarker.lng">确 定</el-button> | |
| 120 | + </span> | |
| 121 | + </el-dialog> | |
| 122 | + | |
| 123 | + <!-- 围栏设置弹窗 --> | |
| 124 | + <el-dialog title="设置电子围栏" :visible.sync="fenceVisible" width="1000px" append-to-body class="map-dialog"> | |
| 125 | + <div class="fence-editor-layout"> | |
| 126 | + <div class="map-side-panel"> | |
| 127 | + <div class="panel-header">图形管理 ({{ fenceBuffer.length }})</div> | |
| 128 | + <div class="shape-list"> | |
| 129 | + <div v-for="(shape, index) in fenceBuffer" :key="index" class="shape-item"> | |
| 130 | + <i :class="getShapeIcon(shape.type)"></i> | |
| 131 | + <span class="shape-name">{{ getShapeName(shape.type) }} {{ index + 1 }}</span> | |
| 132 | + <el-button type="text" icon="el-icon-delete" class="delete-btn" | |
| 133 | + @click="removeBufferShape(index)"></el-button> | |
| 134 | + </div> | |
| 135 | + <div v-if="!fenceBuffer.length" class="empty-text">暂无图形,请在右侧绘制</div> | |
| 136 | + </div> | |
| 137 | + <div class="panel-footer"> | |
| 138 | + <p class="warning-text" v-if="fenceBuffer.length > 1"> | |
| 139 | + <i class="el-icon-warning"></i> 注意:最终只能保留1个围栏 | |
| 140 | + </p> | |
| 141 | + </div> | |
| 142 | + </div> | |
| 143 | + <div class="map-main-area"> | |
| 144 | + <div class="map-toolbar"> | |
| 145 | + <div v-for="tool in fenceTools" :key="tool.id" class="tool-btn" | |
| 146 | + :class="{ active: activeFenceTool === tool.id }" @click="changeFenceTool(tool.id)"> | |
| 147 | + <span class="tool-icon" :class="'tool-icon--' + tool.id"></span> | |
| 148 | + <span class="tool-label">{{ tool.name }}</span> | |
| 149 | + </div> | |
| 150 | + </div> | |
| 151 | + <div id="fence-map" class="map-canvas"></div> | |
| 152 | + </div> | |
| 153 | + </div> | |
| 154 | + <span slot="footer" class="dialog-footer"> | |
| 155 | + <div class="footer-hint" v-if="fenceBuffer.length > 1">请删除多余图形,仅保留一个围栏后再保存</div> | |
| 156 | + <el-button @click="fenceVisible = false">取 消</el-button> | |
| 157 | + <el-button type="primary" @click="confirmFence" :disabled="fenceBuffer.length !== 1">确 定 保 存</el-button> | |
| 158 | + </span> | |
| 159 | + </el-dialog> | |
| 122 | 160 | </el-dialog> |
| 123 | 161 | </template> |
| 162 | + | |
| 124 | 163 | <script> |
| 125 | - import request from '@/utils/request' | |
| 126 | - import { getDictionaryDataSelector } from '@/api/systemData/dictionary' | |
| 127 | - import { previewDataInterface } from '@/api/systemData/dataInterface' | |
| 128 | - export default { | |
| 129 | - components: {}, | |
| 130 | - props: [], | |
| 131 | - data() { | |
| 132 | - return { | |
| 133 | - loading: false, | |
| 134 | - visible: false, | |
| 135 | - isDetail: false, | |
| 136 | - dataForm: { | |
| 137 | - id:'', | |
| 138 | - id:undefined, | |
| 139 | - mdbm:undefined, | |
| 140 | - djmdbh:undefined, | |
| 141 | - djmd:undefined, | |
| 142 | - dm:undefined, | |
| 143 | - cs:undefined, | |
| 144 | - dz:undefined, | |
| 145 | - xm:undefined, | |
| 146 | - dhhm:undefined, | |
| 147 | - zj:undefined, | |
| 148 | - kysj:undefined, | |
| 149 | - zxzt:undefined, | |
| 150 | - gsmc:undefined, | |
| 151 | - fr:undefined, | |
| 152 | - ywsb:undefined, | |
| 153 | - zzrs:0, | |
| 154 | - storeCategory:undefined, | |
| 155 | - storeType:undefined, | |
| 156 | - }, | |
| 157 | - rules: { | |
| 158 | - }, | |
| 159 | - zxztOptions:[{"fullName":"开店","id":"开店"},{"fullName":"闭店","id":"闭店"}], | |
| 160 | - ywsbOptions:[{"fullName":"有","id":"有"},{"fullName":"无","id":"无"}], | |
| 161 | - storeCategoryOptions:[], | |
| 162 | - storeTypeOptions:[], | |
| 163 | - } | |
| 164 | +import request from '@/utils/request' | |
| 165 | + | |
| 166 | +export default { | |
| 167 | + data() { | |
| 168 | + return { | |
| 169 | + loading: false, | |
| 170 | + visible: false, | |
| 171 | + isDetail: false, | |
| 172 | + dataForm: { | |
| 173 | + id: undefined, | |
| 174 | + mdbm: undefined, | |
| 175 | + dm: undefined, | |
| 176 | + dz: undefined, | |
| 177 | + cs: undefined, | |
| 178 | + xm: undefined, | |
| 179 | + dhhm: undefined, | |
| 180 | + zxzt: undefined, | |
| 181 | + storeCategory: undefined, | |
| 182 | + storeType: undefined, | |
| 183 | + longitude: null, | |
| 184 | + latitude: null, | |
| 185 | + fencePolygons: [], | |
| 186 | + }, | |
| 187 | + rules: { | |
| 188 | + mdbm: [{ required: true, message: '请输入门店编码', trigger: 'blur' }], | |
| 189 | + dm: [{ required: true, message: '请输入店名', trigger: 'blur' }], | |
| 190 | + dz: [{ required: true, message: '请输入地址', trigger: 'blur' }], | |
| 191 | + }, | |
| 192 | + zxztOptions: [{ fullName: '开店', id: '开店' }, { fullName: '闭店', id: '闭店' }], | |
| 193 | + storeCategoryOptions: [], | |
| 194 | + storeTypeOptions: [], | |
| 195 | + | |
| 196 | + // 定位弹窗相关 | |
| 197 | + locationVisible: false, | |
| 198 | + locationMap: null, | |
| 199 | + locationMarkerLayer: null, | |
| 200 | + tempMarker: { lng: null, lat: null }, | |
| 201 | + | |
| 202 | + // 围栏弹窗相关 | |
| 203 | + fenceVisible: false, | |
| 204 | + fenceMap: null, | |
| 205 | + fenceEditor: null, | |
| 206 | + fenceDrawLayers: {}, // 绘图图层(只保留多边形 / 圆形) | |
| 207 | + fenceDisplayLayer: null, // 展示层(显示 buffer 中的图形) | |
| 208 | + activeFenceTool: 'polygon', | |
| 209 | + fenceBuffer: [], // 临时存放绘制的多个图形:[{ id, type, points, geometry }] | |
| 210 | + // 目前仅支持多边形 / 圆形两个工具,矩形与椭圆功能取消 | |
| 211 | + fenceTools: [ | |
| 212 | + { id: 'polygon', name: '多边形' }, | |
| 213 | + { id: 'circle', name: '圆形' } | |
| 214 | + ] | |
| 215 | + } | |
| 216 | + }, | |
| 217 | + created() { | |
| 218 | + this.loadStoreCategoryOptions(); | |
| 219 | + this.loadStoreTypeOptions(); | |
| 220 | + }, | |
| 221 | + methods: { | |
| 222 | + loadStoreCategoryOptions() { | |
| 223 | + request({ url: '/api/Extend/lqmdxx/Selector/StoreCategory', method: 'get' }) | |
| 224 | + .then(res => { this.storeCategoryOptions = res.data || []; }); | |
| 164 | 225 | }, |
| 165 | - computed: {}, | |
| 166 | - watch: {}, | |
| 167 | - created() { | |
| 168 | - this.loadStoreCategoryOptions(); | |
| 169 | - this.loadStoreTypeOptions(); | |
| 226 | + loadStoreTypeOptions() { | |
| 227 | + request({ url: '/api/Extend/lqmdxx/Selector/StoreType', method: 'get' }) | |
| 228 | + .then(res => { this.storeTypeOptions = res.data || []; }); | |
| 170 | 229 | }, |
| 171 | - mounted() { | |
| 172 | - }, | |
| 173 | - methods: { | |
| 174 | - goBack() { | |
| 175 | - this.$emit('refresh') | |
| 176 | - }, | |
| 177 | - // 加载门店类别选项 | |
| 178 | - loadStoreCategoryOptions() { | |
| 179 | - request({ | |
| 180 | - url: '/api/Extend/lqmdxx/Selector/StoreCategory', | |
| 181 | - method: 'get' | |
| 182 | - }).then(res => { | |
| 183 | - this.storeCategoryOptions = res.data || []; | |
| 184 | - }).catch(err => { | |
| 185 | - console.error('加载门店类别选项失败:', err); | |
| 186 | - this.storeCategoryOptions = []; | |
| 230 | + | |
| 231 | + init(id, isDetail) { | |
| 232 | + this.dataForm.id = id || 0; | |
| 233 | + this.visible = true; | |
| 234 | + this.isDetail = isDetail || false; | |
| 235 | + this.$nextTick(() => { | |
| 236 | + this.$refs['elForm'].resetFields(); | |
| 237 | + if (this.dataForm.id) { | |
| 238 | + request({ url: '/api/Extend/LqMdxx/' + this.dataForm.id, method: 'get' }) | |
| 239 | + .then(res => { | |
| 240 | + this.dataForm = res.data; | |
| 241 | + }); | |
| 242 | + } | |
| 243 | + }) | |
| 244 | + }, | |
| 245 | + | |
| 246 | + loadTMapScript() { | |
| 247 | + return new Promise((resolve, reject) => { | |
| 248 | + if (window.TMap) return resolve(); | |
| 249 | + const script = document.createElement('script'); | |
| 250 | + script.src = 'https://map.qq.com/api/gljs?v=1.exp&key=YRXBZ-NEV6T-K7SXH-VJPMF-G5IQF-F3FCJ&libraries=tools,geometry'; | |
| 251 | + script.onload = resolve; | |
| 252 | + script.onerror = reject; | |
| 253 | + document.body.appendChild(script); | |
| 254 | + }); | |
| 255 | + }, | |
| 256 | + | |
| 257 | + // --- 门店定位逻辑 --- | |
| 258 | + handleOpenLocation() { | |
| 259 | + this.locationVisible = true; | |
| 260 | + this.tempMarker = { lng: this.dataForm.longitude, lat: this.dataForm.latitude }; | |
| 261 | + this.$nextTick(() => { | |
| 262 | + this.initLocationMap(); | |
| 263 | + }); | |
| 264 | + }, | |
| 265 | + | |
| 266 | + initLocationMap() { | |
| 267 | + this.loadTMapScript().then(() => { | |
| 268 | + const TMap = window.TMap; | |
| 269 | + const centerLat = this.tempMarker.lat || 30.656149; // 成都 | |
| 270 | + const centerLng = this.tempMarker.lng || 104.065735; | |
| 271 | + const center = new TMap.LatLng(centerLat, centerLng); | |
| 272 | + | |
| 273 | + this.locationMap = new TMap.Map('location-map', { center, zoom: 14 }); | |
| 274 | + this.locationMarkerLayer = new TMap.MultiMarker({ map: this.locationMap, geometries: [] }); | |
| 275 | + | |
| 276 | + if (this.tempMarker.lng) { | |
| 277 | + this.locationMarkerLayer.setGeometries([{ id: 'm', position: new TMap.LatLng(this.tempMarker.lat, this.tempMarker.lng) }]); | |
| 278 | + } | |
| 279 | + | |
| 280 | + this.locationMap.on('click', (evt) => { | |
| 281 | + if (!evt.latLng) return; | |
| 282 | + const lat = evt.latLng.getLat(); | |
| 283 | + const lng = evt.latLng.getLng(); | |
| 284 | + this.tempMarker = { lng, lat }; | |
| 285 | + this.locationMarkerLayer.setGeometries([{ id: 'm', position: evt.latLng }]); | |
| 187 | 286 | }); |
| 188 | - }, | |
| 189 | - // 加载门店类型选项 | |
| 190 | - loadStoreTypeOptions() { | |
| 191 | - request({ | |
| 192 | - url: '/api/Extend/lqmdxx/Selector/StoreType', | |
| 193 | - method: 'get' | |
| 194 | - }).then(res => { | |
| 195 | - this.storeTypeOptions = res.data || []; | |
| 196 | - }).catch(err => { | |
| 197 | - console.error('加载门店类型选项失败:', err); | |
| 198 | - this.storeTypeOptions = []; | |
| 287 | + }); | |
| 288 | + }, | |
| 289 | + | |
| 290 | + confirmLocation() { | |
| 291 | + this.dataForm.longitude = this.tempMarker.lng; | |
| 292 | + this.dataForm.latitude = this.tempMarker.lat; | |
| 293 | + this.locationVisible = false; | |
| 294 | + }, | |
| 295 | + | |
| 296 | + // --- 围栏设置逻辑 --- | |
| 297 | + handleOpenFence() { | |
| 298 | + if (!this.dataForm.longitude) return; | |
| 299 | + this.fenceVisible = true; | |
| 300 | + // 初始化 Buffer:历史上如果有多块,只保留第一块,保证始终至多一个围栏 | |
| 301 | + const polygons = Array.isArray(this.dataForm.fencePolygons) ? this.dataForm.fencePolygons : []; | |
| 302 | + if (polygons.length > 0 && Array.isArray(polygons[0])) { | |
| 303 | + this.fenceBuffer = [{ | |
| 304 | + id: `old-${Date.now()}`, | |
| 305 | + type: 'polygon', | |
| 306 | + points: polygons[0] | |
| 307 | + }]; | |
| 308 | + } else { | |
| 309 | + this.fenceBuffer = []; | |
| 310 | + } | |
| 311 | + this.$nextTick(() => { | |
| 312 | + this.initFenceMap(); | |
| 313 | + }); | |
| 314 | + }, | |
| 315 | + | |
| 316 | + initFenceMap() { | |
| 317 | + this.loadTMapScript().then(() => { | |
| 318 | + const TMap = window.TMap; | |
| 319 | + const center = new TMap.LatLng(this.dataForm.latitude, this.dataForm.longitude); | |
| 320 | + this.fenceMap = new TMap.Map('fence-map', { center, zoom: 16 }); | |
| 321 | + | |
| 322 | + // 1. 门店位置固定标点 | |
| 323 | + this.fenceStoreMarkerLayer = new TMap.MultiMarker({ | |
| 324 | + map: this.fenceMap, | |
| 325 | + geometries: [{ id: 'store', position: center }] | |
| 199 | 326 | }); |
| 200 | - }, | |
| 201 | - init(id, isDetail) { | |
| 202 | - this.dataForm.id = id || 0; | |
| 203 | - this.visible = true; | |
| 204 | - this.isDetail = isDetail || false; | |
| 205 | - this.$nextTick(() => { | |
| 206 | - this.$refs['elForm'].resetFields(); | |
| 207 | - if (this.dataForm.id) { | |
| 208 | - request({ | |
| 209 | - url: '/api/Extend/LqMdxx/' + this.dataForm.id, | |
| 210 | - method: 'get' | |
| 211 | - }).then(res =>{ | |
| 212 | - this.dataForm = res.data; | |
| 327 | + | |
| 328 | + // 2. 初始化展示层 | |
| 329 | + this.fenceDisplayLayer = new TMap.MultiPolygon({ | |
| 330 | + map: this.fenceMap, | |
| 331 | + geometries: [], | |
| 332 | + styles: { | |
| 333 | + default: new TMap.PolygonStyle({ | |
| 334 | + color: 'rgba(41,182,246,0.2)', | |
| 335 | + borderColor: 'rgba(41,182,246,0.9)', | |
| 336 | + borderWidth: 2 | |
| 213 | 337 | }) |
| 214 | 338 | } |
| 339 | + }); | |
| 340 | + | |
| 341 | + // 3. 构建临时绘制图层: | |
| 342 | + // - 一个多边形图层 polygonLayer | |
| 343 | + // - 一个圆形图层 circleLayer | |
| 344 | + const polygonLayer = new TMap.MultiPolygon({ | |
| 345 | + map: this.fenceMap, | |
| 346 | + geometries: [], | |
| 347 | + styles: { | |
| 348 | + default: new TMap.PolygonStyle({ | |
| 349 | + color: 'rgba(255,152,0,0.2)', | |
| 350 | + borderColor: '#FF9800', | |
| 351 | + borderWidth: 2 | |
| 352 | + }) | |
| 353 | + } | |
| 354 | + }); | |
| 355 | + const circleLayer = new TMap.MultiCircle({ | |
| 356 | + map: this.fenceMap, | |
| 357 | + geometries: [], | |
| 358 | + styles: { | |
| 359 | + default: new TMap.CircleStyle({ | |
| 360 | + color: 'rgba(255,152,0,0.2)', | |
| 361 | + borderColor: '#FF9800', | |
| 362 | + borderWidth: 2 | |
| 363 | + }) | |
| 364 | + } | |
| 365 | + }); | |
| 366 | + this.fenceDrawLayers = { | |
| 367 | + polygon: polygonLayer, | |
| 368 | + circle: circleLayer | |
| 369 | + }; | |
| 370 | + | |
| 371 | + // 4. 初始化唯一编辑器(后续不再整体销毁,只清空内容) | |
| 372 | + this.fenceEditor = new TMap.tools.GeometryEditor({ | |
| 373 | + map: this.fenceMap, | |
| 374 | + overlayList: [ | |
| 375 | + { overlay: polygonLayer, id: 'polygon' }, | |
| 376 | + { overlay: circleLayer, id: 'circle' } | |
| 377 | + ], | |
| 378 | + actionMode: TMap.tools.constants.EDITOR_ACTION.DRAW, | |
| 379 | + activeOverlayId: 'polygon', | |
| 380 | + snappable: true | |
| 381 | + }); | |
| 382 | + | |
| 383 | + // 5. 监听绘制完毕事件:每次只保留当前绘制结果为唯一围栏 | |
| 384 | + this.fenceEditor.on('draw_complete', (geometry) => { | |
| 385 | + const toolId = this.activeFenceTool; | |
| 386 | + const points = this._extractPoints(toolId, geometry); | |
| 387 | + | |
| 388 | + if (points && points.length >= 3) { | |
| 389 | + // 业务约束:无论之前画了什么,本次绘制即为“唯一围栏” | |
| 390 | + this.fenceBuffer = [{ | |
| 391 | + id: `shape-${Date.now()}`, | |
| 392 | + type: toolId, | |
| 393 | + points: points | |
| 394 | + }]; | |
| 395 | + this.refreshFenceDisplay(); | |
| 396 | + } | |
| 397 | + | |
| 398 | + // 绘制完毕后:不销毁 Editor,只清空对应图层的临时几何 | |
| 399 | + const drawLayer = this.fenceDrawLayers[toolId]; | |
| 400 | + if (drawLayer && typeof drawLayer.setGeometries === 'function') { | |
| 401 | + drawLayer.setGeometries([]); | |
| 402 | + } | |
| 403 | + | |
| 404 | + // 延迟一帧重置绘制状态,避免与内部事件冲突 | |
| 405 | + setTimeout(() => { | |
| 406 | + if (this.fenceEditor && window.TMap && window.TMap.tools && window.TMap.tools.constants) { | |
| 407 | + this.fenceEditor.setActiveOverlay(toolId); | |
| 408 | + this.fenceEditor.setActionMode(window.TMap.tools.constants.EDITOR_ACTION.DRAW); | |
| 409 | + } | |
| 410 | + }, 20); | |
| 411 | + }); | |
| 412 | + | |
| 413 | + this.refreshFenceDisplay(); | |
| 414 | + }); | |
| 415 | + }, | |
| 416 | + | |
| 417 | + changeFenceTool(id) { | |
| 418 | + this.activeFenceTool = id; | |
| 419 | + if (this.fenceEditor) { | |
| 420 | + // 清除可能画了一半的所有绘制层(去重后防止同一图层重复清理) | |
| 421 | + const uniqueLayers = Object.values(this.fenceDrawLayers).filter((layer, index, arr) => arr.indexOf(layer) === index); | |
| 422 | + uniqueLayers.forEach(layer => { | |
| 423 | + if (layer && typeof layer.setGeometries === 'function') { | |
| 424 | + layer.setGeometries([]); | |
| 425 | + } | |
| 426 | + }); | |
| 427 | + | |
| 428 | + this.fenceEditor.setActiveOverlay(id); | |
| 429 | + this.fenceEditor.setActionMode(window.TMap.tools.constants.EDITOR_ACTION.DRAW); | |
| 430 | + } | |
| 431 | + }, | |
| 432 | + | |
| 433 | + refreshFenceDisplay() { | |
| 434 | + if (!this.fenceDisplayLayer) return; | |
| 435 | + const TMap = window.TMap; | |
| 436 | + const geometries = this.fenceBuffer.map(item => { | |
| 437 | + const path = item.points.map(p => new TMap.LatLng(p.lat, p.lng)); | |
| 438 | + // 闭合 | |
| 439 | + if (path[0].getLat() !== path[path.length - 1].getLat() || path[0].getLng() !== path[path.length - 1].getLng()) { | |
| 440 | + path.push(path[0]); | |
| 441 | + } | |
| 442 | + return { id: item.id, paths: [path] }; | |
| 443 | + }); | |
| 444 | + this.fenceDisplayLayer.setGeometries(geometries); | |
| 445 | + }, | |
| 446 | + | |
| 447 | + removeBufferShape(index) { | |
| 448 | + this.fenceBuffer.splice(index, 1); | |
| 449 | + this.refreshFenceDisplay(); | |
| 450 | + }, | |
| 451 | + | |
| 452 | + confirmFence() { | |
| 453 | + if (this.fenceBuffer.length !== 1) { | |
| 454 | + this.$message.warning('请确保最终只保留一个围栏'); | |
| 455 | + return; | |
| 456 | + } | |
| 457 | + this.dataForm.fencePolygons = [this.fenceBuffer[0].points]; | |
| 458 | + this.fenceVisible = false; | |
| 459 | + }, | |
| 460 | + | |
| 461 | + // 将 GeometryEditor 返回的几何统一转换为点数组 | |
| 462 | + _extractPoints(toolId, geometry) { | |
| 463 | + if (!geometry) return [] | |
| 464 | + | |
| 465 | + // 通用:从 geometry 中尝试解析点数组(paths 或 path,一维或二维) | |
| 466 | + let pointsArray = [] | |
| 467 | + if (Array.isArray(geometry.paths) && geometry.paths.length) { | |
| 468 | + const first = geometry.paths[0] | |
| 469 | + pointsArray = Array.isArray(first) ? first : geometry.paths | |
| 470 | + } else if (Array.isArray(geometry.path) && geometry.path.length) { | |
| 471 | + const first = geometry.path[0] | |
| 472 | + pointsArray = Array.isArray(first) ? first : geometry.path | |
| 473 | + } | |
| 474 | + | |
| 475 | + // 多边形 / 矩形 / 椭圆:统一按点数组处理 | |
| 476 | + if (['polygon', 'rectangle', 'ellipse'].includes(toolId)) { | |
| 477 | + if (!Array.isArray(pointsArray) || !pointsArray.length) return [] | |
| 478 | + return pointsArray.map(p => ({ | |
| 479 | + lng: typeof p.getLng === 'function' ? p.getLng() : p.lng, | |
| 480 | + lat: typeof p.getLat === 'function' ? p.getLat() : p.lat | |
| 481 | + })) | |
| 482 | + } | |
| 483 | + | |
| 484 | + // 圆形:用中心 + 半径近似成 36 边多边形 | |
| 485 | + if (toolId === 'circle' && geometry.center && geometry.radius) { | |
| 486 | + const center = geometry.center | |
| 487 | + const r = geometry.radius | |
| 488 | + const cLat = typeof center.getLat === 'function' ? center.getLat() : center.lat | |
| 489 | + const cLng = typeof center.getLng === 'function' ? center.getLng() : center.lng | |
| 490 | + const R = 6378137 | |
| 491 | + const latRad = (cLat * Math.PI) / 180 | |
| 492 | + return Array.from({ length: 36 }, (_, i) => { | |
| 493 | + const angle = (2 * Math.PI * i) / 36 | |
| 494 | + return { | |
| 495 | + lat: cLat + (r * Math.cos(angle)) / R * (180 / Math.PI), | |
| 496 | + lng: cLng + (r * Math.sin(angle)) / (R * Math.cos(latRad)) * (180 / Math.PI) | |
| 497 | + } | |
| 215 | 498 | }) |
| 216 | - }, | |
| 217 | - dataFormSubmit() { | |
| 218 | - this.$refs['elForm'].validate((valid) => { | |
| 219 | - if (valid) { | |
| 220 | - if (!this.dataForm.id) { | |
| 221 | - request({ | |
| 222 | - url: `/api/Extend/LqMdxx`, | |
| 223 | - method: 'post', | |
| 224 | - data: this.dataForm, | |
| 225 | - }).then((res) => { | |
| 226 | - this.$message({ | |
| 227 | - message: res.msg, | |
| 228 | - type: 'success', | |
| 229 | - duration: 1000, | |
| 230 | - onClose: () => { | |
| 231 | - this.visible = false, | |
| 232 | - this.$emit('refresh', true) | |
| 233 | - } | |
| 234 | - }) | |
| 235 | - }) | |
| 236 | - } else { | |
| 237 | - request({ | |
| 238 | - url: '/api/Extend/LqMdxx/' + this.dataForm.id, | |
| 239 | - method: 'PUT', | |
| 240 | - data: this.dataForm | |
| 241 | - }).then((res) => { | |
| 242 | - this.$message({ | |
| 243 | - message: res.msg, | |
| 244 | - type: 'success', | |
| 245 | - duration: 1000, | |
| 246 | - onClose: () => { | |
| 247 | - this.visible = false | |
| 248 | - this.$emit('refresh', true) | |
| 249 | - } | |
| 250 | - }) | |
| 251 | - }) | |
| 252 | - } | |
| 253 | - } | |
| 254 | - }) | |
| 255 | - }, | |
| 499 | + } | |
| 500 | + | |
| 501 | + return [] | |
| 502 | + }, | |
| 503 | + | |
| 504 | + getShapeIcon(type) { | |
| 505 | + const icons = { polygon: 'el-icon-picture', circle: 'el-icon-loading', rectangle: 'el-icon-full-screen', ellipse: 'el-icon-help' }; | |
| 506 | + return icons[type] || 'el-icon-info'; | |
| 507 | + }, | |
| 508 | + | |
| 509 | + getShapeName(type) { | |
| 510 | + const names = { polygon: '多边形', circle: '圆形', rectangle: '矩形', ellipse: '椭圆' }; | |
| 511 | + return names[type] || '图形'; | |
| 512 | + }, | |
| 513 | + | |
| 514 | + dataFormSubmit() { | |
| 515 | + this.$refs['elForm'].validate((valid) => { | |
| 516 | + if (!valid) return; | |
| 517 | + const isNew = !this.dataForm.id; | |
| 518 | + request({ | |
| 519 | + url: isNew ? '/api/Extend/LqMdxx' : `/api/Extend/LqMdxx/${this.dataForm.id}`, | |
| 520 | + method: isNew ? 'POST' : 'PUT', | |
| 521 | + data: this.dataForm | |
| 522 | + }).then((res) => { | |
| 523 | + this.$message({ | |
| 524 | + message: res.msg, | |
| 525 | + type: 'success', | |
| 526 | + duration: 1000, | |
| 527 | + onClose: () => { | |
| 528 | + this.visible = false; | |
| 529 | + this.$emit('refresh', true); | |
| 530 | + } | |
| 531 | + }) | |
| 532 | + }) | |
| 533 | + }) | |
| 256 | 534 | } |
| 257 | 535 | } |
| 536 | +} | |
| 258 | 537 | </script> |
| 538 | + | |
| 539 | +<style lang="scss" scoped> | |
| 540 | +.fence-status-bar { | |
| 541 | + display: flex; | |
| 542 | + align-items: center; | |
| 543 | + padding: 5px 0; | |
| 544 | + | |
| 545 | + .hint-text { | |
| 546 | + font-size: 12px; | |
| 547 | + color: #f56c6c; | |
| 548 | + margin-left: 8px; | |
| 549 | + } | |
| 550 | +} | |
| 551 | + | |
| 552 | +.map-dialog { | |
| 553 | + ::v-deep .el-dialog__body { | |
| 554 | + padding: 10px 20px; | |
| 555 | + } | |
| 556 | +} | |
| 557 | + | |
| 558 | +.map-container-wrapper { | |
| 559 | + .map-header-bar { | |
| 560 | + display: flex; | |
| 561 | + justify-content: space-between; | |
| 562 | + font-size: 13px; | |
| 563 | + color: #606266; | |
| 564 | + margin-bottom: 8px; | |
| 565 | + | |
| 566 | + .coordinate-info { | |
| 567 | + color: #409eff; | |
| 568 | + font-weight: bold; | |
| 569 | + } | |
| 570 | + } | |
| 571 | +} | |
| 572 | + | |
| 573 | +.map-canvas { | |
| 574 | + width: 100%; | |
| 575 | + height: 450px; | |
| 576 | + border-radius: 4px; | |
| 577 | + border: 1px solid #dcdfe6; | |
| 578 | +} | |
| 579 | + | |
| 580 | +.fence-editor-layout { | |
| 581 | + display: flex; | |
| 582 | + height: 500px; | |
| 583 | + gap: 15px; | |
| 584 | + | |
| 585 | + .map-side-panel { | |
| 586 | + width: 220px; | |
| 587 | + border: 1px solid #ebeef5; | |
| 588 | + border-radius: 4px; | |
| 589 | + display: flex; | |
| 590 | + flex-direction: column; | |
| 591 | + | |
| 592 | + .panel-header { | |
| 593 | + padding: 10px; | |
| 594 | + background: #f5f7fa; | |
| 595 | + border-bottom: 1px solid #ebeef5; | |
| 596 | + font-weight: bold; | |
| 597 | + font-size: 14px; | |
| 598 | + } | |
| 599 | + | |
| 600 | + .shape-list { | |
| 601 | + flex: 1; | |
| 602 | + overflow-y: auto; | |
| 603 | + padding: 10px; | |
| 604 | + | |
| 605 | + .shape-item { | |
| 606 | + display: flex; | |
| 607 | + align-items: center; | |
| 608 | + padding: 8px; | |
| 609 | + margin-bottom: 8px; | |
| 610 | + background: #fdfdfd; | |
| 611 | + border: 1px solid #f2f2f2; | |
| 612 | + border-radius: 4px; | |
| 613 | + | |
| 614 | + i { | |
| 615 | + margin-right: 8px; | |
| 616 | + color: #409eff; | |
| 617 | + } | |
| 618 | + | |
| 619 | + .shape-name { | |
| 620 | + flex: 1; | |
| 621 | + font-size: 12px; | |
| 622 | + } | |
| 623 | + | |
| 624 | + .delete-btn { | |
| 625 | + color: #f56c6c; | |
| 626 | + padding: 0; | |
| 627 | + } | |
| 628 | + } | |
| 629 | + | |
| 630 | + .empty-text { | |
| 631 | + text-align: center; | |
| 632 | + color: #909399; | |
| 633 | + font-size: 12px; | |
| 634 | + margin-top: 50px; | |
| 635 | + } | |
| 636 | + } | |
| 637 | + | |
| 638 | + .panel-footer { | |
| 639 | + padding: 10px; | |
| 640 | + border-top: 1px solid #ebeef5; | |
| 641 | + | |
| 642 | + .warning-text { | |
| 643 | + font-size: 11px; | |
| 644 | + color: #e6a23c; | |
| 645 | + margin: 0; | |
| 646 | + } | |
| 647 | + } | |
| 648 | + } | |
| 649 | + | |
| 650 | + .map-main-area { | |
| 651 | + flex: 1; | |
| 652 | + display: flex; | |
| 653 | + flex-direction: column; | |
| 654 | + | |
| 655 | + .map-toolbar { | |
| 656 | + display: flex; | |
| 657 | + padding: 0 0 10px 0; | |
| 658 | + gap: 8px; | |
| 659 | + | |
| 660 | + .tool-btn { | |
| 661 | + display: flex; | |
| 662 | + align-items: center; | |
| 663 | + padding: 5px 12px; | |
| 664 | + border: 1px solid #dcdfe6; | |
| 665 | + border-radius: 4px; | |
| 666 | + cursor: pointer; | |
| 667 | + background: #fff; | |
| 668 | + transition: all 0.2s; | |
| 669 | + | |
| 670 | + &:hover { | |
| 671 | + border-color: #409eff; | |
| 672 | + color: #409eff; | |
| 673 | + } | |
| 674 | + | |
| 675 | + &.active { | |
| 676 | + background: #ecf5ff; | |
| 677 | + border-color: #409eff; | |
| 678 | + color: #409eff; | |
| 679 | + } | |
| 680 | + | |
| 681 | + .tool-icon { | |
| 682 | + width: 16px; | |
| 683 | + height: 16px; | |
| 684 | + margin-right: 6px; | |
| 685 | + background-size: cover; | |
| 686 | + } | |
| 687 | + | |
| 688 | + .tool-label { | |
| 689 | + font-size: 12px; | |
| 690 | + } | |
| 691 | + } | |
| 692 | + | |
| 693 | + .tool-icon--polygon { | |
| 694 | + background-image: url('https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/polygon.png'); | |
| 695 | + } | |
| 696 | + | |
| 697 | + .tool-icon--circle { | |
| 698 | + background-image: url('https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/circle.png'); | |
| 699 | + } | |
| 700 | + | |
| 701 | + .tool-icon--rectangle { | |
| 702 | + background-image: url('https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/rectangle.png'); | |
| 703 | + } | |
| 704 | + | |
| 705 | + .tool-icon--ellipse { | |
| 706 | + background-image: url('https://mapapi.qq.com/web/lbs/javascriptGL/demo/img/ellipse.png'); | |
| 707 | + } | |
| 708 | + } | |
| 709 | + } | |
| 710 | +} | |
| 711 | + | |
| 712 | +.footer-hint { | |
| 713 | + color: #f56c6c; | |
| 714 | + font-size: 12px; | |
| 715 | + margin-right: 20px; | |
| 716 | + display: inline-block; | |
| 717 | +} | |
| 718 | +</style> | |
| 259 | 719 | \ No newline at end of file | ... | ... |