| | |
| | | <template> |
| | | <router-link :to="{ name: 'dashboard' }"> |
| | | <div class="flex logo-container flex-center"> |
| | | <!-- <img class="logo-img" :src="@/assets/img/logo.png" alt="logo" /> --> |
| | | <img v-if="localInfo.logoPath" class="logo-img" :src="localInfo.logoPath" alt="logo" /> |
| | | <img v-else class="logo-img" src="@/assets/img/menuLogo.png" alt="logo" /> |
| | | <img class="logo-img" :src="homeLogo" alt="logo" /> |
| | | <!-- <h1 class="logo-title" v-if="showTitle">{{ title }}</h1> --> |
| | | </div> |
| | | </router-link> |
| | | </template> |
| | | |
| | | <script> |
| | | import homeLogo from '@/assets/img/sdjt.jpg' |
| | | |
| | | export default { |
| | | name: 'Logo', |
| | | |
| | |
| | | }, |
| | | data() { |
| | | return { |
| | | localInfo: {}, |
| | | homeLogo, |
| | | } |
| | | }, |
| | | created() { |
| | | this.localInfo = JSON.parse(localStorage.getItem('localInfo')) |
| | | }, |
| | | } |
| | | </script> |
| | |
| | | } |
| | | } |
| | | </style> |
| | | |
| | |
| | | meta: { |
| | | title: '首页' |
| | | }, |
| | | redirect: '/dashboard/analysis', |
| | | redirect: '/analysisScreen/operationBigdata', |
| | | children: [] |
| | | }, |
| | | { |
| | |
| | | //component: () => import('@/components/layouts/BasicLayout'), |
| | | component: resolve => require(['@/components/layouts/TabLayout'], resolve), |
| | | meta: {title: '首页'}, |
| | | redirect: '/dashboard/analysis', |
| | | redirect: '/analysisScreen/operationBigdata', |
| | | children: [...generateChildRouters(data)] |
| | | }, |
| | | { |
| | |
| | | <template> |
| | | <div> |
| | | <div class="content"> |
| | | <div class="header_ct">{{ localInfo.taskViewName }}</div> |
| | | <div class="header_ct"><span class="blue-text">{{ localInfo.operationViewName }}</span> |
| | | 可视化大屏</div> |
| | | <div class="tab_page"> |
| | | <div class="operation" @click="goOperation">运营数据分析</div> |
| | | <div class="alarm">告警数据分析</div> |
| | |
| | | </div> |
| | | <div class="info_ct"> |
| | | <div class="left_ct"> |
| | | <div class="block alarm_overview"> |
| | | <div class="block alarm_overview" @click="goToAlarmQuery"> |
| | | <div class="block_title"> |
| | | <div> |
| | | <span class="blue_text">告警</span> |
| | |
| | | </div> --> |
| | | </div> |
| | | </div> |
| | | <div class="block latest_alarm"> |
| | | <div class="block latest_alarm" @click="goToAlarmQuery"> |
| | | <div class="block_title"> |
| | | <div> |
| | | <span class="blue_text">最新</span> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="right_ct"> |
| | | <div class="block terminal_statis"> |
| | | <div class="block terminal_statis" @click="goToTerminal"> |
| | | <div class="block_title"> |
| | | <div> |
| | | <span class="blue_text">终端</span> |
| | |
| | | this.getAlarmRankingData() |
| | | }, |
| | | goOperation() { |
| | | // this.$router.replace("operationBigdata") |
| | | window.location.href = window.location.origin + '/analysisScreen/operationBigdata' |
| | | }, |
| | | goToAlarmQuery() { |
| | | window.location.href = window.location.origin + '/event/oilout' |
| | | }, |
| | | goToTerminal() { |
| | | window.location.href = window.location.origin + '/device/camera' |
| | | }, |
| | | //获取告警概况数据 |
| | | getOverviewData() { |
| | |
| | | text-align: center; |
| | | font-size: 40px; |
| | | color: #fff; |
| | | .blue-text { |
| | | color: #5a81f9; |
| | | } |
| | | } |
| | | .tab_page { |
| | | display: flex; |
| | |
| | | </div> |
| | | <img class="title_line" src="@/assets/img/bigdata/daoh.png" alt="" /> |
| | | <div class="icon_ct"> |
| | | <div class="icon_fa"> |
| | | <div class="icon_fa" @click="goToDataAnalysis"> |
| | | <div class="icon_img_fa"> |
| | | <img src="@/assets/img/bigdata/cheliuliang.png" alt="" /> |
| | | </div> |
| | | <div class="icon_name">车流量</div> |
| | | <div class="icon_num">{{ overviewData.trafficFlow }}</div> |
| | | </div> |
| | | <div class="icon_fa"> |
| | | <div class="icon_fa" @click="goToDataAnalysis"> |
| | | <div class="icon_img_fa"> |
| | | <img src="@/assets/img/bigdata/jinzhanshu.png" alt="" /> |
| | | </div> |
| | | <div class="icon_name">进站数</div> |
| | | <div class="icon_num">{{ overviewData.inboundCount }}</div> |
| | | </div> |
| | | <div class="icon_fa"> |
| | | <div class="icon_fa" @click="goToDataAnalysis('oilCount')"> |
| | | <div class="icon_img_fa"> |
| | | <img src="@/assets/img/bigdata/jiayoushuliang.png" alt="" /> |
| | | </div> |
| | |
| | | <div class="pie_self" id="refuelingRateChart"></div> |
| | | </div> |
| | | </div> |
| | | <div class="block customer_statis"> |
| | | <div class="block customer_statis" @click="goToCarInfo"> |
| | | <div class="block_title"> |
| | | <div> |
| | | <span class="blue_text">客户</span> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="block traffic_statis"> |
| | | <div class="block traffic_statis" @click="goToDataAnalysis"> |
| | | <div class="block_title"> |
| | | <div> |
| | | <span class="blue_text">车流量</span> |
| | |
| | | <!-- <img class="person_img" src="@/assets/img/bigdata/person.png" alt="" /> |
| | | <img class="car_img" src="@/assets/img/bigdata/car.png" alt="" /> --> |
| | | </div> |
| | | <div class="block refueling_statis"> |
| | | <div class="block refueling_statis" @click="goToDataAnalysis"> |
| | | <div class="block_title"> |
| | | <div> |
| | | <span class="blue_text">加油</span> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="block vehicle_type_distribution"> |
| | | <div class="block vehicle_type_distribution" @click="goToCarInfo"> |
| | | <div class="block_title"> |
| | | <div> |
| | | <span class="blue_text">车型</span> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="block monthly_sales_statis"> |
| | | <div class="block monthly_sales_statis" @click="goToSales"> |
| | | <div class="block_title"> |
| | | <div> |
| | | <span class="blue_text">月度销售</span> |
| | |
| | | goAlarmPage() { |
| | | window.location.href = window.location.origin + '/analysisScreen/alarmBigdata' |
| | | }, |
| | | goToDataAnalysis(target) { |
| | | const scrollTarget = target === 'oilCount' ? 'oil-count-card' : 'traffic-flow-card' |
| | | localStorage.setItem('scrollToTarget', scrollTarget) |
| | | if (target === 'oilCount') { |
| | | localStorage.setItem('trendAnalysisType', '1') |
| | | } |
| | | window.location.href = window.location.origin + '/data/DataReport' |
| | | }, |
| | | goToCarInfo() { |
| | | window.location.href = window.location.origin + '/data/carInfo' |
| | | }, |
| | | goToSales() { |
| | | window.location.href = window.location.origin + '/data/sales' |
| | | }, |
| | | //获取运营概况数据 |
| | | getOverviewData() { |
| | | getAction('/jyz/operationData/overview', {}).then((res) => { |
| | |
| | | <a-tab-pane key="2" tab="活动配置" force-render> |
| | | <ActiveCom></ActiveCom> |
| | | </a-tab-pane> |
| | | <a-tab-pane key="3" tab="机构标签" force-render> |
| | | <DepartLabel></DepartLabel> |
| | | </a-tab-pane> |
| | | </a-tabs> |
| | | </div> |
| | | </template> |
| | |
| | | <script> |
| | | import ActiveCom from './components/ActiveCom' |
| | | import CustTypeConfig from './components/CustTypeConfig' |
| | | import DepartLabel from './components/DepartLabel' |
| | | |
| | | export default { |
| | | name: 'ActivityList', |
| | |
| | | components: { |
| | | ActiveCom, |
| | | CustTypeConfig, |
| | | DepartLabel, |
| | | }, |
| | | |
| | | data() { |
| | |
| | | /> |
| | | </a-form-item> |
| | | <a-form-item label="客户类型"> |
| | | <j-dict-select-tag |
| | | <a-select |
| | | style="width: 180px" |
| | | v-model="queryParam.clientType" |
| | | placeholder="请选择" |
| | | dictCode="client_type" |
| | | /> |
| | | allowClear |
| | | > |
| | | <a-select-option v-for="item in clientTypeOptions" :key="item.id" :value="item.id"> |
| | | {{ item.clientName }} |
| | | </a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | <!-- <a-select style="width: 180px" allowClear v-model="queryParam.clientType" placeholder="请选择客户类型"> |
| | | <a-select-option :value="1"> 普通客户 </a-select-option> |
| | | <a-select-option :value="2"> 潜在客户 </a-select-option> |
| | |
| | | description: 'CarInfo列表', |
| | | queryParam: {}, |
| | | tagList: [], //所有标签集合 |
| | | clientTypeOptions: [], //客户类型列表 |
| | | // 表头 |
| | | columns: [ |
| | | { |
| | |
| | | }, |
| | | |
| | | methods: { |
| | | // 获取客户类型列表 |
| | | getClientTypeOptions() { |
| | | getAction('/jyz/client/listClientType').then((res) => { |
| | | if (res.code === 200) { |
| | | this.clientTypeOptions = res.result |
| | | } |
| | | }) |
| | | }, |
| | | delSlectTag(item) { |
| | | console.log(item) |
| | | let index = this.selectTagList.findIndex((el) => el == item) |
| | |
| | | this.queryParam.orgCode = node.node.dataRef.orgCode |
| | | this.loadData() |
| | | }, |
| | | // 获取客户类型列表 |
| | | getClientTypeOptions() { |
| | | getAction('/jyz/client/listClientType').then((res) => { |
| | | if (res.code === 200) { |
| | | this.clientTypeOptions = res.result |
| | | } |
| | | }) |
| | | }, |
| | | }, |
| | | |
| | | created() { |
| | | this.getTagInfo(true) |
| | | this.getClientTypeOptions() |
| | | }, |
| | | } |
| | | </script> |
| | |
| | | <a v-if="new Date().getTime() < Date.parse(record.startTime)" @click="handleEdit(record)">编辑</a> |
| | | <a v-else disabled>编辑</a> |
| | | <a-divider type="vertical" /> |
| | | <a @click="viewVehicleFrequency(record)">查看车辆加油频次</a> |
| | | <a-divider type="vertical" /> |
| | | <a-popconfirm title="确定删除吗?" @confirm="() => handleDelete(record.id)"> |
| | | <a>删除</a> |
| | | </a-popconfirm> |
| | |
| | | <script> |
| | | import ActivityModal from './ActivityModal' |
| | | import { JeecgListMixin } from '@tievd/cube-block/lib/mixins/JeecgListMixin' |
| | | import { getAction } from '@tievd/cube-block/lib/api/manage' |
| | | |
| | | export default { |
| | | name: 'ActivityList', |
| | |
| | | delete: '/jyz/activity/delete', |
| | | deleteBatch: '/jyz/activity/deleteBatch', |
| | | exportXlsUrl: '/jyz/activity/exportXls', |
| | | getVehicleFrequency: '/jyz/activity/getVehicleFrequency', |
| | | }, |
| | | } |
| | | }, |
| | | |
| | | methods: {}, |
| | | methods: { |
| | | viewVehicleFrequency(record) { |
| | | console.log('查看车辆加油频次', record) |
| | | getAction(this.url.getVehicleFrequency, { actId: record.id }).then((res) => { |
| | | if (res.code === 200) { |
| | | const result = res.result |
| | | const activity = result.activity || {} |
| | | const beforeData = result.beforeActivity || {} |
| | | const duringData = result.duringActivity || {} |
| | | const afterData = result.afterActivity || {} |
| | | |
| | | const formatNumber = (num) => num ? Number(num).toFixed(2) : '0.00' |
| | | |
| | | const content = ` |
| | | <div style="padding: 10px;"> |
| | | <h3 style="text-align: center; margin-bottom: 15px;">${activity.name || '活动'}</h3> |
| | | <p style="text-align: center; color: #666; margin-bottom: 20px;"> |
| | | 活动时间:${activity.startTime} ~ ${activity.endTime} |
| | | </p> |
| | | |
| | | <table style="width: 100%; border-collapse: collapse; margin-bottom: 20px;"> |
| | | <thead> |
| | | <tr style="background-color: #f5f5f5;"> |
| | | <th style="border: 1px solid #ddd; padding: 8px; text-align: center;">统计项</th> |
| | | <th style="border: 1px solid #ddd; padding: 8px; text-align: center;">活动前</th> |
| | | <th style="border: 1px solid #ddd; padding: 8px; text-align: center;">活动中</th> |
| | | <th style="border: 1px solid #ddd; padding: 8px; text-align: center;">活动后</th> |
| | | </tr> |
| | | </thead> |
| | | <tbody> |
| | | <tr> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center; font-weight: bold;">加油车辆数</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center;">${beforeData.totalVehicles || 0} 辆</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center; color: #1890ff;">${duringData.totalVehicles || 0} 辆</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center;">${afterData.totalVehicles || 0} 辆</td> |
| | | </tr> |
| | | <tr> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center; font-weight: bold;">加油记录数</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center;">${beforeData.totalOilRecords || 0} 笔</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center; color: #1890ff;">${duringData.totalOilRecords || 0} 笔</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center;">${afterData.totalOilRecords || 0} 笔</td> |
| | | </tr> |
| | | <tr> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center; font-weight: bold;">平均加油量</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center;">${formatNumber(beforeData.avgOilVolume)} 升</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center; color: #1890ff;">${formatNumber(duringData.avgOilVolume)} 升</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center;">${formatNumber(afterData.avgOilVolume)} 升</td> |
| | | </tr> |
| | | <tr> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center; font-weight: bold;">总加油量</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center;">${beforeData.totalOilVolume || 0} 升</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center; color: #1890ff;">${duringData.totalOilVolume || 0} 升</td> |
| | | <td style="border: 1px solid #ddd; padding: 8px; text-align: center;">${afterData.totalOilVolume || 0} 升</td> |
| | | </tr> |
| | | </tbody> |
| | | </table> |
| | | |
| | | <p style="text-align: center; color: #999; font-size: 12px; margin-top: 10px;"> |
| | | 注:活动期间数据以蓝色高亮显示 |
| | | </p> |
| | | </div> |
| | | ` |
| | | this.$info({ |
| | | title: '活动加油频次对比统计', |
| | | content: this.$createElement('div', { domProps: { innerHTML: content } }), |
| | | width: 700, |
| | | okText: '确定', |
| | | }) |
| | | } else { |
| | | this.$message.error(res.message || '查询失败') |
| | | } |
| | | }).catch((err) => { |
| | | console.error(err) |
| | | this.$message.error('查询失败') |
| | | }) |
| | | }, |
| | | }, |
| | | |
| | | created() {}, |
| | | } |
| | |
| | | <script> |
| | | import CustTypeModal from './CustTypeModal' |
| | | import { JeecgListMixin } from '@tievd/cube-block/lib/mixins/JeecgListMixin' |
| | | import { UI_CACHE_DB_DICT_DATA } from '@tievd/cube-block/lib/store/mutation-types' |
| | | import { deleteAction, getAction } from '@tievd/cube-block/lib/api/manage' |
| | | import Vue from 'vue' |
| | | |
| | | export default { |
| | | name: 'ActivityList', |
| | |
| | | }, |
| | | |
| | | methods: { |
| | | modalFormOk() { |
| | | this.loadData() |
| | | this.refreshDictCache() |
| | | }, |
| | | refreshDictCache() { |
| | | getAction('/sys/dict/queryAllDictItems').then((res) => { |
| | | if (res.success) { |
| | | Vue.ls.remove(UI_CACHE_DB_DICT_DATA) |
| | | Vue.ls.set(UI_CACHE_DB_DICT_DATA, res.result, 7 * 24 * 60 * 60 * 1000) |
| | | } |
| | | }) |
| | | }, |
| | | handleDelete(id) { |
| | | // 删除前清除前端字典缓存 |
| | | // 删除后刷新页面以更新字典缓存 |
| | | deleteAction(this.url.delete, { id: id }) |
| | | .then((res) => { |
| | | if (res.success) { |
| | | this.$message.success(res.message) |
| | | this.loadData() |
| | | this.refreshDictCache() |
| | | } else { |
| | | this.$message.warning(res.message) |
| | | } |
| | | }) |
| | | .finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | transformConfigText(obj) { |
| | | var text = '' |
| | | if (obj.timeStr == '1,YEARS') { |
| | |
| | | v-model="clientConfigsList.clientName" |
| | | ></a-input> |
| | | </a-form-item> |
| | | <div v-if="!clientConfigsList.clientConfigs || clientConfigsList.clientConfigs.length === 0" style="text-align: center"> |
| | | <a-button type="primary" @click="addConditions"> 添加规则 </a-button> |
| | | </div> |
| | | <div v-for="(subItem, subIndex) in clientConfigsList.clientConfigs" :key="subIndex"> |
| | | <div style="display: flex"> |
| | | <div>条件{{ subIndex + 1 }}:</div> |
| | |
| | | <a-select-option value="1,YEARS"> 近1年 </a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="加油频次"> |
| | | <!-- 规则类型选择:加油频次 OR 加油趋势 --> |
| | | <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="规则类型"> |
| | | <a-select |
| | | :disabled="subItem.timeStr == '7,DAYS' || subItem.timeStr == '30,DAYS'" |
| | | style="width: 300px" |
| | | v-model="subItem.countType" |
| | | v-model="subItem.ruleType" |
| | | placeholder="请选择" |
| | | @change="ruleTypeChange($event, subIndex)" |
| | | > |
| | | <a-select-option :value="1"> 累计 </a-select-option> |
| | | <a-select-option :value="2"> 每月 </a-select-option> |
| | | <a-select-option :value="1"> 加油频次 </a-select-option> |
| | | <a-select-option :value="2"> 加油趋势 </a-select-option> |
| | | </a-select> |
| | | <div style="display: flex"> |
| | | <a-select style="width: 150px" v-model="subItem.countRef" placeholder="请选择"> |
| | | <a-select-option :value="1"> 大于 </a-select-option> |
| | | <a-select-option :value="0"> 等于 </a-select-option> |
| | | <a-select-option :value="-1"> 小于 </a-select-option> |
| | | </a-form-item> |
| | | <!-- 加油频次配置(ruleType=1时显示) --> |
| | | <div v-show="subItem.ruleType == 1"> |
| | | <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="加油频次"> |
| | | <a-select |
| | | :disabled="subItem.timeStr == '7,DAYS' || subItem.timeStr == '30,DAYS'" |
| | | style="width: 300px" |
| | | v-model="subItem.countType" |
| | | placeholder="请选择" |
| | | > |
| | | <a-select-option :value="1"> 累计 </a-select-option> |
| | | <a-select-option :value="2"> 每月 </a-select-option> |
| | | </a-select> |
| | | <div style="display: flex"> |
| | | <a-select style="width: 150px" v-model="subItem.countRef" placeholder="请选择"> |
| | | <a-select-option :value="1"> 大于 </a-select-option> |
| | | <a-select-option :value="0"> 等于 </a-select-option> |
| | | <a-select-option :value="-1"> 小于 </a-select-option> |
| | | </a-select> |
| | | <a-input |
| | | style="width: 150px" |
| | | placeholder="请输入次数" |
| | | onkeyup="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}" |
| | | onafterpaste="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'0')}else{this.value=this.value.replace(/\D/g,'')}" |
| | | v-model="subItem.countNum" |
| | | ></a-input> |
| | | </div> |
| | | </a-form-item> |
| | | </div> |
| | | <!-- 加油趋势配置(ruleType=2时显示) --> |
| | | <div v-show="subItem.ruleType == 2"> |
| | | <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="加油趋势"> |
| | | <a-select |
| | | style="width: 300px" |
| | | v-model="subItem.countTrend" |
| | | placeholder="请选择" |
| | | > |
| | | <a-select-option :value="1"> 稳定 </a-select-option> |
| | | <a-select-option :value="2"> 减少 </a-select-option> |
| | | </a-select> |
| | | <div v-if="subItem.countTrend == 1" style="color: #999; font-size: 12px; margin-top: 5px;"> |
| | | 稳定:历史月加油次数≥次数 且 近期月加油次数≥次数 |
| | | </div> |
| | | <div v-if="subItem.countTrend == 2" style="color: #999; font-size: 12px; margin-top: 5px;"> |
| | | 减少:历史月加油次数≥次数 但 近期月加油次数<次数 |
| | | </div> |
| | | </a-form-item> |
| | | <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="历史月数"> |
| | | <a-select |
| | | style="width: 140px" |
| | | v-model="subItem.historyMonths" |
| | | placeholder="历史月数" |
| | | > |
| | | <a-select-option :value="3"> 3个月 </a-select-option> |
| | | <a-select-option :value="6"> 6个月 </a-select-option> |
| | | <a-select-option :value="9"> 9个月 </a-select-option> |
| | | <a-select-option :value="12"> 12个月 </a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="近期月数"> |
| | | <a-select |
| | | style="width: 140px" |
| | | v-model="subItem.recentMonths" |
| | | placeholder="近期月数" |
| | | > |
| | | <a-select-option :value="1"> 1个月 </a-select-option> |
| | | <a-select-option :value="2"> 2个月 </a-select-option> |
| | | <a-select-option :value="3"> 3个月 </a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | <a-form-item :labelCol="labelCol" :wrapperCol="wrapperCol" label="固定加油次数"> |
| | | <a-input |
| | | style="width: 150px" |
| | | placeholder="请输入次数" |
| | | placeholder="每月固定次数" |
| | | onkeyup="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'')}else{this.value=this.value.replace(/\D/g,'')}" |
| | | onafterpaste="if(this.value.length==1){this.value=this.value.replace(/[^1-9]/g,'0')}else{this.value=this.value.replace(/\D/g,'')}" |
| | | v-model="subItem.countNum" |
| | | ></a-input> |
| | | </div> |
| | | </a-form-item> |
| | | </div> |
| | | <div style="text-align: center"> |
| | | <a-button type="primary" @click="addConditions"> 增加条件 </a-button> |
| | | </a-form-item> |
| | | </div> |
| | | <div style="text-align: center" v-if="subIndex === clientConfigsList.clientConfigs.length - 1"> |
| | | <a-button type="primary" @click="addConditions"> 增加条件 </a-button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </a-spin> |
| | |
| | | |
| | | methods: { |
| | | moment, |
| | | buildCondition(condition = {}) { |
| | | return Object.assign( |
| | | { |
| | | ruleType: 1, |
| | | timeStr: '3,MONTHS', |
| | | countType: 2, |
| | | countRef: 1, |
| | | countNum: 3, |
| | | countTrend: null, |
| | | historyMonths: null, |
| | | recentMonths: null, |
| | | }, |
| | | condition |
| | | ) |
| | | }, |
| | | //删除条件 |
| | | delConditions(index) { |
| | | console.log(index) |
| | |
| | | this.clientConfigsList.clientConfigs[index].countType = 1 |
| | | } |
| | | }, |
| | | trendChange(e, index) { |
| | | // 选择趋势时,设置默认值 |
| | | if (e > 0) { |
| | | this.clientConfigsList.clientConfigs[index].historyMonths = 3 |
| | | this.clientConfigsList.clientConfigs[index].recentMonths = 3 |
| | | } else { |
| | | // 清除趋势配置 |
| | | this.clientConfigsList.clientConfigs[index].historyMonths = null |
| | | this.clientConfigsList.clientConfigs[index].recentMonths = null |
| | | } |
| | | }, |
| | | ruleTypeChange(e, index) { |
| | | let target = this.clientConfigsList.clientConfigs[index] |
| | | // 切换规则类型时,清空对应的配置 |
| | | if (e == 1) { |
| | | // 加油频次:清除趋势配置 |
| | | Object.assign(target, { |
| | | ruleType: 1, |
| | | countTrend: null, |
| | | historyMonths: null, |
| | | recentMonths: null, |
| | | countType: target.countType || 2, |
| | | countRef: target.countRef || 1, |
| | | }) |
| | | } else if (e == 2) { |
| | | // 加油趋势:清除频次配置,设置默认值 |
| | | Object.assign(target, { |
| | | ruleType: 2, |
| | | countType: 2, |
| | | countRef: 1, |
| | | countTrend: target.countTrend || 1, |
| | | historyMonths: target.historyMonths || 3, |
| | | recentMonths: target.recentMonths || 3, |
| | | }) |
| | | } |
| | | }, |
| | | //新增条件 |
| | | addConditions(index) { |
| | | console.log(index) |
| | | this.clientConfigsList.clientConfigs.push({}) |
| | | this.clientConfigsList.clientConfigs.push(this.buildCondition()) |
| | | }, |
| | | disabledDate(current) { |
| | | // Can not select days before today and today |
| | |
| | | add() { |
| | | this.edit({ |
| | | clientName: '', |
| | | clientConfigs: [{}], |
| | | clientConfigs: [this.buildCondition()], |
| | | }) |
| | | }, |
| | | edit(record) { |
| | |
| | | // } |
| | | this.visible = true |
| | | this.model = Object.assign({}, record) |
| | | this.clientConfigsList = record |
| | | this.clientConfigsList = Object.assign({}, record, { |
| | | clientConfigs: (record.clientConfigs || []).length |
| | | ? record.clientConfigs.map((item) => this.buildCondition(item)) |
| | | : [], |
| | | }) |
| | | }, |
| | | close() { |
| | | this.$emit('close') |
| | |
| | | if (JSON.stringify(el) == '{}') { |
| | | isEmpty = true |
| | | } |
| | | for (let k in el) { |
| | | if (el[k] == '' || el[k].length == '0') { |
| | | // 根据规则类型校验 |
| | | if (el.ruleType == 1) { |
| | | // 加油频次:检查时间范围、次数类型、关系、次数 |
| | | if (!el.timeStr || !el.countRef || !el.countType || !el.countNum) { |
| | | isEmpty = true |
| | | } |
| | | } |
| | | if (!el.timeStr || !el.countRef || !el.countType || !el.countNum) { |
| | | isEmpty = true |
| | | if (el.countRef == 0 || el.countNum == 0) { |
| | | isEmpty = false |
| | | } else if (el.ruleType == 2) { |
| | | // 加油趋势:检查时间范围、趋势、历史月数、近期月数、次数 |
| | | if (!el.timeStr || !el.countTrend || !el.historyMonths || !el.recentMonths || !el.countNum) { |
| | | isEmpty = true |
| | | } |
| | | } else { |
| | | // 未选择规则类型 |
| | | isEmpty = true |
| | | } |
| | | }) |
| | | if (isEmpty) { |
| | |
| | | <template> |
| | | <div style="margin-right: 30px" class="data-reMechanism"> |
| | | <div style="margin-right: 30px" class="data-reMechanism" :class="{ 'trend-detail-open': trendDetailVisible }"> |
| | | <div style="display: flex; align-items: center"> |
| | | <div style="margin-right:12px;font-size:16px;font-weight:bold">趋势分析</div> |
| | | <a-radio-group v-model="trendAnalysisType" @change="trendAnalysisTypeChange"> |
| | |
| | | <a-radio-button :value="3"> 通过率 </a-radio-button> |
| | | <a-radio-button :value="4"> 客户 </a-radio-button> |
| | | <a-radio-button :value="5"> 流失率 </a-radio-button> |
| | | <a-radio-button :value="6"> 销售金额 </a-radio-button> |
| | | </a-radio-group> |
| | | </div> |
| | | <div> |
| | | <div style="display: flex; align-items: flex-end; flex-direction: column"> |
| | | <div class="trend-analysis-chart" :class="{ 'is-modal-open': trendDetailVisible }" style="display: flex; align-items: flex-end; flex-direction: column"> |
| | | <a-radio-group |
| | | style="margin: 20px 0" |
| | | size="small" |
| | |
| | | :chartData="trendAnalysisData" |
| | | style="width: 100%" |
| | | :showpercent="trendAnalysisChartShowPercent" |
| | | :enablePointClick="true" |
| | | @chart-click="handleTrendChartClick" |
| | | ></DataReLineChart> |
| | | </div> |
| | | <div id="trendAnalysisChartID"></div> |
| | |
| | | <a-range-picker style="width: 300px; margin: 0 12px" v-if="isContrast" @change="contrastTimeChange" /> |
| | | </div> |
| | | <div class="fueling-overview-block"> |
| | | <div class="fueling-overview-item"> |
| | | <div class="fueling-overview-item" id="traffic-flow-card"> |
| | | <div class="fueling-overview-name">车流量</div> |
| | | <div class="fueling-overview-num"> |
| | | <span style="margin-right: 24px">{{ statisTotalObj.carCount }}</span> |
| | |
| | | <span style="color: #16b777" v-if="isContrast">{{ contrastObj.appearCount }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="fueling-overview-item"> |
| | | <div class="fueling-overview-item" id="oil-count-card"> |
| | | <div class="fueling-overview-name">加油数</div> |
| | | <div class="fueling-overview-num"> |
| | | <span style="margin-right: 24px">{{ statisTotalObj.oilCount }}</span> |
| | |
| | | <span style="color: #16b777" v-if="isContrast">{{ contrastObj.rebackRate }}%</span> |
| | | </div> |
| | | </div> |
| | | <div class="fueling-overview-item"> |
| | | <div class="fueling-overview-name">销售金额(元)</div> |
| | | <div class="fueling-overview-num"> |
| | | <span style="margin-right: 24px">{{ statisTotalObj.totalAmount }}</span> |
| | | <span style="color: #16b777" v-if="isContrast">{{ contrastObj.totalAmount }}</span> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="oil-freq-compare-ct"> |
| | | <div class="block-title">活动前后车辆加油频次</div> |
| | | <div class="oil-freq-compare-operator"> |
| | | <span>活动前时段</span> |
| | | <a-range-picker |
| | | style="width: 360px; margin: 0 12px" |
| | | :value="beforeTimeRange" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | valueFormat="YYYY-MM-DD HH:mm:ss" |
| | | :show-time="{ defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')] }" |
| | | @change="beforeTimeChange" |
| | | /> |
| | | <span>活动后时段</span> |
| | | <a-range-picker |
| | | style="width: 360px; margin: 0 12px" |
| | | :value="afterTimeRange" |
| | | format="YYYY-MM-DD HH:mm:ss" |
| | | valueFormat="YYYY-MM-DD HH:mm:ss" |
| | | :show-time="{ defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')] }" |
| | | @change="afterTimeChange" |
| | | /> |
| | | <a-button type="primary" @click="getOilFreqCompareData"> 统计频次 </a-button> |
| | | </div> |
| | | <a-table |
| | | class="oil-freq-compare-table" |
| | | rowKey="licenseNum" |
| | | size="middle" |
| | | :columns="oilFreqCompareColumns" |
| | | :dataSource="oilFreqCompareData" |
| | | :loading="oilFreqCompareLoading" |
| | | :scroll="{ y: 420 }" |
| | | :pagination="{ pageSize: 10, showSizeChanger: true, pageSizeOptions: ['10', '20', '50'] }" |
| | | > |
| | | </a-table> |
| | | </div> |
| | | <div> |
| | | <div class="block-title">车型分析</div> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div> |
| | | <div class="block-title">油品类型分析</div> |
| | | <div style="display: flex"> |
| | | <DataReLineChart |
| | | style="width: 70%" |
| | | domId="oilTypeLine" |
| | | ref="oilTypeLineRef" |
| | | :chartData="oilTypeLineData" |
| | | ></DataReLineChart> |
| | | <div style="width: 30%"> |
| | | <a-radio-group v-model="oilTypeSpecies" size="small" @change="oilTypeSpeciesChange"> |
| | | <a-radio-button :value="5"> 油品销量 </a-radio-button> |
| | | <a-radio-button :value="6"> 销售金额 </a-radio-button> |
| | | </a-radio-group> |
| | | <DataRePieChart |
| | | style="width: 100%" |
| | | domId="oilTypePie" |
| | | ref="oilTypePieRef" |
| | | :chartData="oilTypePieData" |
| | | ></DataRePieChart> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <TopTenCustomers |
| | | :customerData="customerObj" |
| | | :typeOneTotal="customerOneTotal" |
| | | :typeTwoTotal="customerTwoTotal" |
| | | ></TopTenCustomers> |
| | | <a-modal |
| | | centered |
| | | width="70%" |
| | | :footer="null" |
| | | v-model="trendDetailVisible" |
| | | :title="trendDetailTitle" |
| | | :zIndex="5000" |
| | | :getContainer="getModalContainer" |
| | | wrapClassName="trend-detail-modal-wrap" |
| | | @cancel="closeTrendDetail" |
| | | > |
| | | <a-table |
| | | size="middle" |
| | | bordered |
| | | :columns="trendDetailColumns" |
| | | :dataSource="trendDetailData" |
| | | :loading="trendDetailLoading" |
| | | :scroll="{ y: 420 }" |
| | | :pagination="{ pageSize: 10, showSizeChanger: true, pageSizeOptions: ['10', '20', '50'] }" |
| | | :rowKey=" |
| | | (record, index) => { |
| | | return record.id || record.licenseNum || record.captureTime || record.startTime || index |
| | | } |
| | | " |
| | | > |
| | | </a-table> |
| | | </a-modal> |
| | | </div> |
| | | </template> |
| | | |
| | |
| | | import DataReLineChart from './DataReLineChart' |
| | | import DataRePieChart from './DataRePieChart' |
| | | import { getAction, postAction } from '@tievd/cube-block/lib/api/manage' |
| | | import moment from 'moment' |
| | | export default { |
| | | name: 'DataReMechanism', |
| | | components: { |
| | |
| | | description: '数据报表(加油站)', |
| | | vehicleTypeSpecies: 1, |
| | | fuelingStationSpecies: 2, |
| | | oilTypeSpecies: 5, |
| | | searchWay: '1', |
| | | isContrast: false, |
| | | activeTime: '', |
| | |
| | | vehicleTypePieData: {}, //车型分析饼图数据 |
| | | fuelLevelLineData: {}, //加油位分析折线图数据 |
| | | fuelLevelPieData: {}, //加油位分析饼图数据 |
| | | oilTypeLineData: {}, //油品类型分析折线图数据 |
| | | oilTypePieData: {}, //油品类型分析饼图数据 |
| | | vehicleTypeLineData: {}, //车型分析折线图数据 |
| | | trendAnalysisType: 0, //趋势分析选择类型 |
| | | trendAnalysisUnit: 'DAYS', //趋势分析选择时间 |
| | | trendAnalysisData: {}, //趋势分析数据 |
| | | trendAnalysisChart: null, //趋势分析图表 |
| | | trendDetailVisible: false, |
| | | trendDetailTitle: '趋势分析明细', |
| | | trendDetailLoading: false, |
| | | trendDetailData: [], |
| | | trendDetailColumns: [], |
| | | beforeStartTime: '', |
| | | beforeEndTime: '', |
| | | afterStartTime: '', |
| | | afterEndTime: '', |
| | | beforeTimeRange: [], |
| | | afterTimeRange: [], |
| | | oilFreqCompareLoading: false, |
| | | oilFreqCompareData: [], |
| | | oilFreqCompareColumns: [ |
| | | { |
| | | title: '车牌号', |
| | | align: 'center', |
| | | dataIndex: 'licenseNum', |
| | | }, |
| | | { |
| | | title: '活动前加油次数', |
| | | align: 'center', |
| | | dataIndex: 'beforeOilCount', |
| | | }, |
| | | { |
| | | title: '活动后加油次数', |
| | | align: 'center', |
| | | dataIndex: 'afterOilCount', |
| | | }, |
| | | { |
| | | title: '变化值', |
| | | align: 'center', |
| | | dataIndex: 'diffOilCount', |
| | | }, |
| | | { |
| | | title: '变化率', |
| | | align: 'center', |
| | | dataIndex: 'diffRate', |
| | | customRender: function (t) { |
| | | if (t === null || t === undefined) { |
| | | return '--' |
| | | } |
| | | return t + '%' |
| | | }, |
| | | }, |
| | | ], |
| | | } |
| | | }, |
| | | |
| | | methods: { |
| | | moment, |
| | | getModalContainer() { |
| | | return document.body |
| | | }, |
| | | getTrendDetailColumnsByType() { |
| | | let commonIndexColumn = { |
| | | title: '序号', |
| | | dataIndex: '', |
| | | key: 'rowIndex', |
| | | width: 80, |
| | | align: 'center', |
| | | customRender: function (t, r, index) { |
| | | return parseInt(index) + 1 |
| | | }, |
| | | } |
| | | if (this.trendAnalysisType == 0) { |
| | | return [ |
| | | commonIndexColumn, |
| | | { title: '抓拍时间', align: 'center', dataIndex: 'captureTime' }, |
| | | { title: '车流量', align: 'center', dataIndex: 'carCount' }, |
| | | { title: '车型编码', align: 'center', dataIndex: 'modelCode' }, |
| | | { title: '设备编码', align: 'center', dataIndex: 'cameraCode' }, |
| | | ] |
| | | } |
| | | if (this.trendAnalysisType == 4 || this.trendAnalysisType == 5) { |
| | | return [ |
| | | commonIndexColumn, |
| | | { title: '更新时间', align: 'center', dataIndex: 'updateTimeSelf' }, |
| | | { title: '车牌号', align: 'center', dataIndex: 'licenseNum' }, |
| | | { title: '客户类型', align: 'center', dataIndex: 'clientName' }, |
| | | { title: '累计加油次数', align: 'center', dataIndex: 'oilCount' }, |
| | | { title: '累计油品销量', align: 'center', dataIndex: 'oilSum' }, |
| | | ] |
| | | } |
| | | if (this.trendAnalysisType == 6) { |
| | | return [ |
| | | commonIndexColumn, |
| | | { title: '进站时间', align: 'center', dataIndex: 'startTime' }, |
| | | { title: '车牌号', align: 'center', dataIndex: 'licenseNum' }, |
| | | { title: '行为类型', align: 'center', dataIndex: 'behaviorText' }, |
| | | { title: '加油位', align: 'center', dataIndex: 'oilPosition' }, |
| | | { title: '油品销量', align: 'center', dataIndex: 'oilVolume' }, |
| | | { title: '销售金额(元)', align: 'center', dataIndex: 'totalAmount' }, |
| | | { title: '通过率(分)', align: 'center', dataIndex: 'spandTime' }, |
| | | ] |
| | | } |
| | | return [ |
| | | commonIndexColumn, |
| | | { title: '进站时间', align: 'center', dataIndex: 'startTime' }, |
| | | { title: '车牌号', align: 'center', dataIndex: 'licenseNum' }, |
| | | { title: '行为类型', align: 'center', dataIndex: 'behaviorText' }, |
| | | { title: '加油位', align: 'center', dataIndex: 'oilPosition' }, |
| | | { title: '油品销量', align: 'center', dataIndex: 'oilVolume' }, |
| | | { title: '通过率(分)', align: 'center', dataIndex: 'spandTime' }, |
| | | ] |
| | | }, |
| | | handleTrendChartClick(params) { |
| | | if (!params || !params.name) { |
| | | return |
| | | } |
| | | if (this.$refs.trendAnalysisLineRef && this.$refs.trendAnalysisLineRef.hideTooltip) { |
| | | this.$refs.trendAnalysisLineRef.hideTooltip() |
| | | } |
| | | this.trendDetailVisible = true |
| | | this.trendDetailTitle = '趋势分析明细 - ' + params.name |
| | | this.trendDetailColumns = this.getTrendDetailColumnsByType() |
| | | this.trendDetailLoading = true |
| | | postAction('/jyz/dataTable/statTrendDetail', { |
| | | orgCode: this.selectTreeObj.orgCode, |
| | | startTime: this.startTime, |
| | | endTime: this.endTime, |
| | | trendType: this.trendAnalysisType, |
| | | timeUnit: this.trendAnalysisUnit, |
| | | statTime: params.name, |
| | | seriesName: params.seriesName, |
| | | }) |
| | | .then((res) => { |
| | | this.trendDetailData = res.result || [] |
| | | }) |
| | | .finally(() => { |
| | | this.trendDetailLoading = false |
| | | }) |
| | | }, |
| | | closeTrendDetail() { |
| | | this.trendDetailVisible = false |
| | | this.trendDetailData = [] |
| | | }, |
| | | //获取车型/加油位折线图数据 |
| | | getVehicleTypeLineData() { |
| | | postAction('/jyz/dataTable/statisMidTable', { |
| | |
| | | this.fuelingStationSpecies = e.target.value |
| | | this.getFuelingStationPieData() |
| | | }, |
| | | //获取油品类型饼图数据 |
| | | getOilTypePieData() { |
| | | postAction('/jyz/dataTable/statFan', { |
| | | orgCode: this.selectTreeObj.orgCode, |
| | | startTime: this.startTime, |
| | | endTime: this.endTime, |
| | | type: this.oilTypeSpecies, |
| | | }).then((res) => { |
| | | console.log(res) |
| | | this.oilTypePieData = res.result |
| | | this.$nextTick(() => { |
| | | this.$refs.oilTypePieRef.setChart() |
| | | }) |
| | | }) |
| | | }, |
| | | //获取油品类型柱状图数据 |
| | | getOilTypeLineData() { |
| | | const barType = this.oilTypeSpecies == 5 ? 3 : 4 |
| | | postAction('/jyz/dataTable/statBar', { |
| | | orgCode: this.selectTreeObj.orgCode, |
| | | startTime: this.startTime, |
| | | endTime: this.endTime, |
| | | type: barType, |
| | | }).then((res) => { |
| | | console.log(res) |
| | | this.oilTypeLineData = res.result |
| | | this.$nextTick(() => { |
| | | this.$refs.oilTypeLineRef.setChart() |
| | | }) |
| | | }) |
| | | }, |
| | | oilTypeSpeciesChange(e) { |
| | | this.oilTypeSpecies = e.target.value |
| | | this.getOilTypePieData() |
| | | this.getOilTypeLineData() |
| | | }, |
| | | //趋势分析类型切换 |
| | | trendAnalysisTypeChange(e) { |
| | | let val = e.target.value |
| | | if (val == 0 || val == 2 || val == 3 || val == 5) { |
| | | if (val == 0 || val == 2 || val == 3 || val == 5 || val == 6) { |
| | | this.trendAnalysisChartShowPercent = true |
| | | } else { |
| | | this.trendAnalysisChartShowPercent = false |
| | |
| | | this.contrastObj = res.result |
| | | }) |
| | | }, |
| | | beforeTimeChange(e, t) { |
| | | this.beforeTimeRange = t |
| | | this.beforeStartTime = t && t.length ? t[0] : '' |
| | | this.beforeEndTime = t && t.length ? t[1] : '' |
| | | }, |
| | | afterTimeChange(e, t) { |
| | | this.afterTimeRange = t |
| | | this.afterStartTime = t && t.length ? t[0] : '' |
| | | this.afterEndTime = t && t.length ? t[1] : '' |
| | | }, |
| | | setOilFreqDefaultRangeByActivity(activity) { |
| | | if (!activity || !activity.startTime || !activity.endTime) { |
| | | return |
| | | } |
| | | this.afterStartTime = activity.startTime |
| | | this.afterEndTime = activity.endTime |
| | | this.afterTimeRange = [this.afterStartTime, this.afterEndTime] |
| | | let startMoment = moment(activity.startTime) |
| | | let endMoment = moment(activity.endTime) |
| | | let spanSeconds = endMoment.diff(startMoment, 'seconds') |
| | | if (spanSeconds <= 0) { |
| | | return |
| | | } |
| | | this.beforeEndTime = startMoment.format('YYYY-MM-DD HH:mm:ss') |
| | | this.beforeStartTime = startMoment.clone().subtract(spanSeconds, 'seconds').format('YYYY-MM-DD HH:mm:ss') |
| | | this.beforeTimeRange = [this.beforeStartTime, this.beforeEndTime] |
| | | }, |
| | | getOilFreqCompareData() { |
| | | if (!this.beforeStartTime || !this.beforeEndTime || !this.afterStartTime || !this.afterEndTime) { |
| | | this.oilFreqCompareData = [] |
| | | return |
| | | } |
| | | this.oilFreqCompareLoading = true |
| | | postAction('/jyz/dataTable/statisOilFreqCompare', { |
| | | orgCode: this.selectTreeObj.orgCode, |
| | | beforeStartTime: this.beforeStartTime, |
| | | beforeEndTime: this.beforeEndTime, |
| | | afterStartTime: this.afterStartTime, |
| | | afterEndTime: this.afterEndTime, |
| | | }) |
| | | .then((res) => { |
| | | this.oilFreqCompareData = res.result || [] |
| | | }) |
| | | .finally(() => { |
| | | this.oilFreqCompareLoading = false |
| | | }) |
| | | }, |
| | | activeTimeChange(e) { |
| | | console.log(e) |
| | | if (e) { |
| | | let item = this.activeOptions.find((el) => el.id == e) |
| | | this.startTime = item.startTime |
| | | this.endTime = item.endTime |
| | | this.setOilFreqDefaultRangeByActivity(item) |
| | | } else { |
| | | this.startTime = '' |
| | | this.endTime = '' |
| | | this.beforeStartTime = '' |
| | | this.beforeEndTime = '' |
| | | this.afterStartTime = '' |
| | | this.afterEndTime = '' |
| | | this.beforeTimeRange = [] |
| | | this.afterTimeRange = [] |
| | | this.oilFreqCompareData = [] |
| | | } |
| | | }, |
| | | activeChange() {}, |
| | |
| | | this.getVehicleTypeLineData() |
| | | this.getVehicleStationPieData() |
| | | this.getFuelingStationPieData() |
| | | this.getOilTypePieData() |
| | | this.getOilTypeLineData() |
| | | this.getTrendAnalysis() |
| | | this.getOilFreqCompareData() |
| | | }, |
| | | initData() { |
| | | this.getCustomerData() |
| | |
| | | this.getVehicleTypeLineData() |
| | | this.getVehicleStationPieData() |
| | | this.getFuelingStationPieData() |
| | | this.getOilTypePieData() |
| | | this.getOilTypeLineData() |
| | | this.getTrendAnalysis() |
| | | }, |
| | | }, |
| | |
| | | created() { |
| | | this.initData() |
| | | }, |
| | | mounted() { |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | const scrollTarget = localStorage.getItem('scrollToTarget') |
| | | if (scrollTarget) { |
| | | const element = document.getElementById(scrollTarget) |
| | | if (element) { |
| | | element.scrollIntoView({ behavior: 'smooth', block: 'center' }) |
| | | localStorage.removeItem('scrollToTarget') |
| | | } |
| | | } |
| | | const trendType = localStorage.getItem('trendAnalysisType') |
| | | if (trendType) { |
| | | this.trendAnalysisType = parseInt(trendType) |
| | | localStorage.removeItem('trendAnalysisType') |
| | | this.getTrendAnalysis() |
| | | } |
| | | }, 500) |
| | | }) |
| | | }, |
| | | } |
| | | </script> |
| | | <style scoped lang="less"> |
| | | @import '~@assets/less/common.less'; |
| | | /deep/ .ant-table-content { |
| | | height: 500px; |
| | | height: auto; |
| | | .ant-table-placeholder { |
| | | background: none; |
| | | border: none; |
| | |
| | | background: #fff; |
| | | } |
| | | .data-reMechanism { |
| | | position: relative; |
| | | isolation: isolate; |
| | | > div { |
| | | position: relative; |
| | | z-index: 2; |
| | | } |
| | | > div:nth-child(2) { |
| | | z-index: 1; |
| | | } |
| | | &.trend-detail-open { |
| | | pointer-events: none; |
| | | } |
| | | .trend-analysis-chart { |
| | | position: relative; |
| | | z-index: 1; |
| | | overflow: hidden; |
| | | } |
| | | .trend-analysis-chart.is-modal-open { |
| | | pointer-events: none; |
| | | } |
| | | .table-operator { |
| | | position: relative; |
| | | z-index: 2; |
| | | margin: 24px 0 0 0; |
| | | } |
| | | .block-title { |
| | |
| | | font-size: 16px; |
| | | } |
| | | .fueling-overview-ct { |
| | | position: relative; |
| | | z-index: 2; |
| | | // margin: 0 12px; |
| | | .fueling-overview-block { |
| | | display: flex; |
| | |
| | | } |
| | | } |
| | | } |
| | | .oil-freq-compare-ct { |
| | | position: relative; |
| | | z-index: 2; |
| | | margin: 8px 12px 24px 12px; |
| | | .oil-freq-compare-operator { |
| | | display: flex; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | gap: 8px 0; |
| | | margin: 12px 0; |
| | | } |
| | | .oil-freq-compare-table { |
| | | /deep/ .ant-table-body { |
| | | min-height: 240px; |
| | | } |
| | | /deep/ .ant-table-pagination.ant-pagination { |
| | | margin: 12px 0 4px 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | | <style lang="less"> |
| | | .trend-detail-modal-wrap { |
| | | z-index: 5000 !important; |
| | | } |
| | | .trend-detail-modal-wrap + .ant-modal-mask { |
| | | z-index: 4999 !important; |
| | | } |
| | | </style> |
| | |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | enablePointClick: { |
| | | type: Boolean, |
| | | default: false, |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | chartEntity: null, |
| | | zrClickHandler: null, |
| | | zrMoveHandler: null, |
| | | zrMouseOutHandler: null, |
| | | lastHoverIndex: -1, |
| | | lastEmitName: '', |
| | | lastEmitAt: 0, |
| | | lastLegendToggleAt: 0, |
| | | } |
| | | }, |
| | | methods: { |
| | | getOffsetPoint(event) { |
| | | if (!event) { |
| | | return null |
| | | } |
| | | if (event.offsetX !== undefined && event.offsetY !== undefined) { |
| | | return [event.offsetX, event.offsetY] |
| | | } |
| | | if (event.event && event.event.offsetX !== undefined && event.event.offsetY !== undefined) { |
| | | return [event.event.offsetX, event.event.offsetY] |
| | | } |
| | | return null |
| | | }, |
| | | isPointInGrid(point) { |
| | | if (!this.chartEntity || !this.chartEntity.containPixel || !point) { |
| | | return false |
| | | } |
| | | return this.chartEntity.containPixel({ gridIndex: 0 }, point) |
| | | }, |
| | | getXData() { |
| | | return this.chartData && this.chartData.xData ? this.chartData.xData : [] |
| | | }, |
| | | resolveNearestIndexByOffsetX(offsetX) { |
| | | let xData = this.getXData() |
| | | if (!this.chartEntity || !this.chartEntity.convertToPixel || !xData.length) { |
| | | return -1 |
| | | } |
| | | let nearestIndex = -1 |
| | | let minDistance = Number.MAX_VALUE |
| | | for (let i = 0; i < xData.length; i++) { |
| | | let xPixel = this.chartEntity.convertToPixel({ xAxisIndex: 0 }, xData[i]) |
| | | if (Array.isArray(xPixel)) { |
| | | xPixel = xPixel[0] |
| | | } |
| | | if (xPixel === undefined || xPixel === null || Number.isNaN(Number(xPixel))) { |
| | | continue |
| | | } |
| | | let distance = Math.abs(Number(offsetX) - Number(xPixel)) |
| | | if (distance < minDistance) { |
| | | minDistance = distance |
| | | nearestIndex = i |
| | | } |
| | | } |
| | | if (nearestIndex === -1) { |
| | | return -1 |
| | | } |
| | | return nearestIndex |
| | | }, |
| | | resolveClickIndex(event, params) { |
| | | let xData = this.getXData() |
| | | if (!xData.length) { |
| | | return -1 |
| | | } |
| | | let point = this.getOffsetPoint(event) |
| | | if (!this.isPointInGrid(point)) { |
| | | return -1 |
| | | } |
| | | if (params && typeof params.dataIndex === 'number' && xData[params.dataIndex] !== undefined) { |
| | | return params.dataIndex |
| | | } |
| | | if (params && params.name) { |
| | | let nameIndex = xData.findIndex((item) => item === params.name) |
| | | if (nameIndex > -1) { |
| | | return nameIndex |
| | | } |
| | | } |
| | | if (this.chartEntity && this.chartEntity.convertFromPixel && event) { |
| | | let xAxisValue = this.chartEntity.convertFromPixel({ xAxisIndex: 0 }, [event.offsetX, event.offsetY]) |
| | | if (xAxisValue !== undefined && xAxisValue !== null && !Number.isNaN(Number(xAxisValue))) { |
| | | let xIndex = Math.round(Number(xAxisValue)) |
| | | if (xIndex >= 0 && xIndex < xData.length) { |
| | | return xIndex |
| | | } |
| | | } |
| | | } |
| | | if (event && event.offsetX !== undefined && event.offsetX !== null) { |
| | | return this.resolveNearestIndexByOffsetX(event.offsetX) |
| | | } |
| | | return -1 |
| | | }, |
| | | emitChartClickByIndex(index, seriesName) { |
| | | let xData = this.getXData() |
| | | if (index < 0 || index >= xData.length) { |
| | | return |
| | | } |
| | | let clickName = xData[index] |
| | | let now = Date.now() |
| | | if (this.lastEmitName === clickName && now - this.lastEmitAt < 120) { |
| | | return |
| | | } |
| | | this.lastEmitName = clickName |
| | | this.lastEmitAt = now |
| | | this.$emit('chart-click', { |
| | | name: clickName, |
| | | seriesName: seriesName || null, |
| | | }) |
| | | }, |
| | | hideTooltip() { |
| | | if (!this.chartEntity) { |
| | | return |
| | | } |
| | | this.chartEntity.dispatchAction({ |
| | | type: 'hideTip', |
| | | }) |
| | | }, |
| | | bindChartClick() { |
| | | if (!this.chartEntity) { |
| | | return |
| | | } |
| | | this.chartEntity.off('click') |
| | | if (this.chartEntity.getZr) { |
| | | let zr = this.chartEntity.getZr() |
| | | if (this.zrClickHandler) { |
| | | zr.off('click', this.zrClickHandler) |
| | | } |
| | | if (this.zrMoveHandler) { |
| | | zr.off('mousemove', this.zrMoveHandler) |
| | | } |
| | | if (this.zrMouseOutHandler) { |
| | | zr.off('globalout', this.zrMouseOutHandler) |
| | | } |
| | | } |
| | | if (!this.enablePointClick) { |
| | | let chartDom = document.getElementById(this.domId) |
| | | if (chartDom) { |
| | | chartDom.style.cursor = 'default' |
| | | } |
| | | return |
| | | } |
| | | let chartDom = document.getElementById(this.domId) |
| | | if (chartDom) { |
| | | chartDom.style.cursor = 'pointer' |
| | | } |
| | | this.chartEntity.on('legendselectchanged', () => { |
| | | this.lastLegendToggleAt = Date.now() |
| | | }) |
| | | this.chartEntity.on('click', (params) => { |
| | | if (params && params.componentType === 'legend') { |
| | | return |
| | | } |
| | | if (Date.now() - this.lastLegendToggleAt < 180) { |
| | | return |
| | | } |
| | | let index = this.resolveClickIndex(params && params.event ? params.event : null, params) |
| | | this.emitChartClickByIndex(index, params ? params.seriesName : null) |
| | | }) |
| | | this.zrMoveHandler = (event) => { |
| | | let xData = this.getXData() |
| | | if (!xData.length) { |
| | | return |
| | | } |
| | | let xIndex = this.resolveClickIndex(event, null) |
| | | if (xIndex < 0 || xIndex >= xData.length) { |
| | | return |
| | | } |
| | | if (this.lastHoverIndex === xIndex) { |
| | | return |
| | | } |
| | | this.lastHoverIndex = xIndex |
| | | this.chartEntity.dispatchAction({ |
| | | type: 'showTip', |
| | | seriesIndex: 0, |
| | | dataIndex: xIndex, |
| | | }) |
| | | } |
| | | this.zrMouseOutHandler = () => { |
| | | this.lastHoverIndex = -1 |
| | | this.hideTooltip() |
| | | } |
| | | this.zrClickHandler = (event) => { |
| | | if (Date.now() - this.lastLegendToggleAt < 180) { |
| | | return |
| | | } |
| | | let index = this.resolveClickIndex(event, null) |
| | | this.emitChartClickByIndex(index, null) |
| | | } |
| | | let zr = this.chartEntity.getZr() |
| | | zr.on('mousemove', this.zrMoveHandler) |
| | | zr.on('globalout', this.zrMouseOutHandler) |
| | | zr.on('click', this.zrClickHandler) |
| | | }, |
| | | setChart() { |
| | | if (!this.chartEntity) { |
| | | let chartDom = document.getElementById(this.domId) |
| | |
| | | var option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | confine: true, |
| | | extraCssText: 'pointer-events:none;', |
| | | axisPointer: { |
| | | // 坐标轴指示器,坐标轴触发有效 |
| | | type: 'line', // 默认为直线,可选为:'line' | 'shadow' |
| | |
| | | shadowBlur: 10, |
| | | formatter: (params) => { |
| | | let strName1 = params[0].name |
| | | let value1 = params[0].value |
| | | let value2 = params[1] ? params[1].value : '未开启' |
| | | return `<div style="color:#fff;font-size:16px;">${strName1}</div> |
| | | <div><span style="color:#fff;display: inline-block;width: 86px;">${ |
| | | this.chartData.barName |
| | | }</span><span style="color:#5DB6FB">${value1} |
| | | </span></div><div><span style="color:#fff;display: inline-block;width: 86px;">${ |
| | | this.chartData.lineName |
| | | }</span><span style="color:#5DB6FB">${value2}${this.showpercent ? '%' : ''}` |
| | | let result = `<div style="color:#fff;font-size:16px;">${strName1}</div>` |
| | | |
| | | if (params[0] && this.chartData.barName) { |
| | | let value1 = params[0].value !== undefined ? params[0].value : 0 |
| | | result += `<div><span style="color:#fff;display: inline-block;width: 86px;">${this.chartData.barName}</span><span style="color:#5DB6FB">${value1}</span></div>` |
| | | } |
| | | |
| | | if (params[1] && this.chartData.lineName) { |
| | | let value2 = params[1].value !== undefined ? params[1].value : 0 |
| | | result += `<div><span style="color:#fff;display: inline-block;width: 86px;">${this.chartData.lineName}</span><span style="color:#5DB6FB">${value2}</span></div>` |
| | | } |
| | | |
| | | if (params[2] && this.chartData.barName2) { |
| | | let value3 = params[2].value !== undefined ? params[2].value : 0 |
| | | result += `<div><span style="color:#fff;display: inline-block;width: 86px;">${this.chartData.barName2}</span><span style="color:#FF6B6B">${value3}</span></div>` |
| | | } |
| | | |
| | | if (params[3] && this.chartData.lineName2) { |
| | | let value4 = params[3].value !== undefined ? params[3].value : 0 |
| | | result += `<div><span style="color:#fff;display: inline-block;width: 86px;">${this.chartData.lineName2}</span><span style="color:#FFD93D">${value4}</span></div>` |
| | | } |
| | | |
| | | return result |
| | | }, |
| | | textStyle: { |
| | | rich: { |
| | |
| | | }, |
| | | axisLabel: { |
| | | show: true, |
| | | formatter: '{value} %', //右侧Y轴文字显示 |
| | | formatter: (value, index) => { |
| | | return value + (this.showpercent ? '%' : '') |
| | | }, |
| | | formatter: this.showpercent ? '{value} %' : '{value}', |
| | | textStyle: { |
| | | color: 'rgba(185, 185, 185, 1)', |
| | | }, |
| | |
| | | }, |
| | | ], |
| | | |
| | | series: [ |
| | | { |
| | | name: this.chartData.barName, |
| | | type: 'bar', |
| | | barWidth: '12px', |
| | | itemStyle: { |
| | | normal: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: '#5B81F9', |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: '#151A22', |
| | | }, |
| | | ]), |
| | | }, |
| | | }, |
| | | data: this.chartData.barData, |
| | | }, |
| | | |
| | | { |
| | | name: this.chartData.lineName, |
| | | type: 'line', |
| | | // smooth: true, |
| | | yAxisIndex: 1, //使用的 y 轴的 index,在单个图表实例中存在多个 y轴的时候有用 |
| | | |
| | | symbol: 'emptycircle', //标记的图形为实心圆 |
| | | symbolSize: 0, //标记的大小 |
| | | areaStyle: { |
| | | normal: { |
| | | color: { |
| | | type: 'linear', |
| | | x: 0, |
| | | y: 0, |
| | | x2: 0, |
| | | y2: 1, |
| | | colorStops: [ |
| | | { |
| | | offset: 0, |
| | | color: 'rgba(87, 147, 67, .5)', // 0% 处的颜色 |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: 'rgba(87, 147, 67, 0.1)', // 100% 处的颜色 |
| | | }, |
| | | ], |
| | | global: false, // 缺省为 false |
| | | }, |
| | | }, |
| | | }, |
| | | itemStyle: { |
| | | color: 'rgba(65, 197, 95, 1)', |
| | | }, |
| | | |
| | | data: this.chartData.lineData, |
| | | }, |
| | | ], |
| | | series: [], |
| | | } |
| | | |
| | | if (this.chartData.barData) { |
| | | option.series.push({ |
| | | name: this.chartData.barName, |
| | | type: 'bar', |
| | | barWidth: '12px', |
| | | itemStyle: { |
| | | normal: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: '#5B81F9', |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: '#151A22', |
| | | }, |
| | | ]), |
| | | }, |
| | | }, |
| | | data: this.chartData.barData, |
| | | }) |
| | | } |
| | | |
| | | if (this.chartData.lineData) { |
| | | option.series.push({ |
| | | name: this.chartData.lineName, |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | smooth: true, |
| | | symbol: 'circle', |
| | | symbolSize: 6, |
| | | lineStyle: { |
| | | width: 2, |
| | | color: '#16B777', |
| | | }, |
| | | itemStyle: { |
| | | color: '#16B777', |
| | | }, |
| | | data: this.chartData.lineData, |
| | | }) |
| | | } |
| | | |
| | | if (this.chartData.barData2) { |
| | | option.series.push({ |
| | | name: this.chartData.barName2, |
| | | type: 'bar', |
| | | barWidth: '12px', |
| | | itemStyle: { |
| | | normal: { |
| | | color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
| | | { |
| | | offset: 0, |
| | | color: '#FF6B6B', |
| | | }, |
| | | { |
| | | offset: 1, |
| | | color: '#8B0000', |
| | | }, |
| | | ]), |
| | | }, |
| | | }, |
| | | data: this.chartData.barData2, |
| | | }) |
| | | } |
| | | |
| | | if (this.chartData.lineData2) { |
| | | option.series.push({ |
| | | name: this.chartData.lineName2, |
| | | type: 'line', |
| | | yAxisIndex: 1, |
| | | smooth: true, |
| | | symbol: 'circle', |
| | | symbolSize: 6, |
| | | lineStyle: { |
| | | width: 2, |
| | | color: '#FFD93D', |
| | | }, |
| | | itemStyle: { |
| | | color: '#FFD93D', |
| | | }, |
| | | data: this.chartData.lineData2, |
| | | }) |
| | | } |
| | | |
| | | option && this.chartEntity.setOption(option, true) |
| | | this.bindChartClick() |
| | | }, |
| | | setPieUpChart() { |
| | | if (!this.chartEntity) { |
| | |
| | | var option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | confine: true, |
| | | extraCssText: 'pointer-events:none;', |
| | | axisPointer: { |
| | | // 坐标轴指示器,坐标轴触发有效 |
| | | type: 'line', // 默认为直线,可选为:'line' | 'shadow' |
| | |
| | | series: [...data], |
| | | } |
| | | option && this.chartEntity.setOption(option, true) |
| | | this.bindChartClick() |
| | | }, |
| | | setOnlyLineChart() { |
| | | if (!this.chartEntity) { |
| | |
| | | var option = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | confine: true, |
| | | extraCssText: 'pointer-events:none;', |
| | | axisPointer: { |
| | | // 坐标轴指示器,坐标轴触发有效 |
| | | type: 'line', // 默认为直线,可选为:'line' | 'shadow' |
| | |
| | | ], |
| | | } |
| | | option && this.chartEntity.setOption(option, true) |
| | | this.bindChartClick() |
| | | }, |
| | | }, |
| | | } |
| | |
| | | <style lang="less" scoped> |
| | | .line-chart { |
| | | height: 20vh; |
| | | position: relative; |
| | | overflow: hidden; |
| | | } |
| | | </style> |
| | |
| | | <a-range-picker style="width: 300px; margin: 0 12px" v-if="isContrast" @change="contrastTimeChange" /> |
| | | </div> |
| | | <div class="fueling-overview-block"> |
| | | <div class="fueling-overview-item"> |
| | | <div class="fueling-overview-item" id="traffic-flow-card"> |
| | | <div class="fueling-overview-name">车流量</div> |
| | | <div class="fueling-overview-num"> |
| | | <span style="margin-right: 24px">{{ statisTotalObj.carCount }}</span> |
| | |
| | | <span style="color: #16b777" v-if="isContrast">{{ contrastObj.appearCount }}</span> |
| | | </div> |
| | | </div> |
| | | <div class="fueling-overview-item"> |
| | | <div class="fueling-overview-item" id="oil-count-card"> |
| | | <div class="fueling-overview-name">加油数</div> |
| | | <div class="fueling-overview-num"> |
| | | <span style="margin-right: 24px">{{ statisTotalObj.oilCount }}</span> |
| | |
| | | created() { |
| | | this.initData() |
| | | }, |
| | | mounted() { |
| | | this.$nextTick(() => { |
| | | setTimeout(() => { |
| | | const scrollTarget = localStorage.getItem('scrollToTarget') |
| | | if (scrollTarget) { |
| | | const element = document.getElementById(scrollTarget) |
| | | if (element) { |
| | | element.scrollIntoView({ behavior: 'smooth', block: 'center' }) |
| | | localStorage.removeItem('scrollToTarget') |
| | | } |
| | | } |
| | | }, 500) |
| | | }) |
| | | }, |
| | | } |
| | | </script> |
| | | <style scoped lang="less"> |
| | |
| | | legend: { |
| | | orient: 'vertical', |
| | | icon: 'circle', |
| | | right: '0', |
| | | top: '10%', |
| | | right: '2%', |
| | | top: 'middle', |
| | | itemWidth: 14, |
| | | itemGap: 20, |
| | | itemGap: 12, |
| | | textStyle: { |
| | | rich: { |
| | | a: { |
| | |
| | | emphasis: { |
| | | scale: false, |
| | | }, |
| | | center: ['40%', '50%'], |
| | | center: ['28%', '52%'], |
| | | top: 'center', |
| | | right: '60%', |
| | | right: '62%', |
| | | label: { |
| | | show: true, |
| | | position: 'inside', |
| New file |
| | |
| | | <template> |
| | | <div class="depart-label-container"> |
| | | <div class="table-page-search-wrapper"> |
| | | <a-form layout="inline"> |
| | | <a-row :gutter="24"> |
| | | <a-col :md="6" :sm="12"> |
| | | <a-form-item label="标签名称"> |
| | | <a-select v-model="queryParam.labelName" placeholder="请选择标签" allowClear @change="searchQuery"> |
| | | <a-select-option value="">全部</a-select-option> |
| | | <a-select-option v-for="label in labelList" :key="label" :value="label"> |
| | | {{ label }} |
| | | </a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | </a-col> |
| | | <a-col :md="6" :sm="12"> |
| | | <a-button type="primary" @click="searchQuery" icon="search">查询</a-button> |
| | | <a-button type="primary" @click="searchReset" icon="reload" style="margin-left: 8px">重置</a-button> |
| | | </a-col> |
| | | </a-row> |
| | | </a-form> |
| | | </div> |
| | | |
| | | <div class="table-operator"> |
| | | <div class="data-overview-ct"> |
| | | <div class="block-title" style="display: flex; align-items: center"> |
| | | <div style="margin-right: 12px">数据对比</div> |
| | | <a-switch v-model="isContrast" /> |
| | | <a-range-picker |
| | | style="width: 300px; margin: 0 12px" |
| | | v-if="isContrast" |
| | | v-model="contrastTimeRange" |
| | | @change="contrastTimeChange" |
| | | :placeholder="['开始时间', '结束时间']" |
| | | /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="chart-container" v-if="isContrast && lineChartData.xData && lineChartData.xData.length > 0"> |
| | | <div class="chart-block"> |
| | | <div class="block-title">数据对比柱状图</div> |
| | | <DataReLineChart |
| | | ref="lineChart" |
| | | :chartData="lineChartData" |
| | | :showpercent="true" |
| | | :domId="'departLabelLineChart'" |
| | | ></DataReLineChart> |
| | | </div> |
| | | </div> |
| | | |
| | | <a-table |
| | | :columns="columns" |
| | | :data-source="dataSource" |
| | | :pagination="pagination" |
| | | :loading="loading" |
| | | @change="handleTableChange" |
| | | :scroll="{ y: 350 }" |
| | | > |
| | | <span slot="action" slot-scope="text, record"> |
| | | <a @click="handleAdd(record)" style="margin-right: 8px">添加标签</a> |
| | | <a-dropdown> |
| | | <a-menu slot="overlay"> |
| | | <a-menu-item v-for="label in getLabels(record)" :key="label" @click="() => handleMenuClick(record, label)"> |
| | | 删除{{ label }} |
| | | </a-menu-item> |
| | | </a-menu> |
| | | <a slot="default">删除标签</a> |
| | | </a-dropdown> |
| | | </span> |
| | | </a-table> |
| | | |
| | | <DepartLabelModal |
| | | ref="modalForm" |
| | | @ok="modalFormOk" |
| | | :parentCode="queryParam.parentCode" |
| | | :parentId="queryParam.parentId" |
| | | ></DepartLabelModal> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getAction, deleteAction, putAction, postAction } from '@tievd/cube-block/lib/api/manage' |
| | | import DepartLabelModal from './DepartLabelModal' |
| | | import DataReLineChart from './DataReLineChart' |
| | | |
| | | export default { |
| | | name: 'DepartLabel', |
| | | components: { |
| | | DepartLabelModal, |
| | | DataReLineChart |
| | | }, |
| | | data() { |
| | | return { |
| | | queryParam: { |
| | | labelName: '', |
| | | parentCode: JSON.parse(localStorage.getItem("userDepartInfo")).orgCode, |
| | | parentId: JSON.parse(localStorage.getItem("userDepartInfo")).id |
| | | }, |
| | | labelList: [], |
| | | dataSource: [], |
| | | loading: false, |
| | | isContrast: false, |
| | | statisTotalObj: {}, |
| | | contrastObj: {}, |
| | | contrastStartTime: '', |
| | | contrastEndTime: '', |
| | | contrastTimeRange: [], |
| | | lineChartData: {}, |
| | | currentOrgData: [], |
| | | contrastOrgData: [], |
| | | pagination: { |
| | | current: 1, |
| | | pageSize: 10, |
| | | total: 0, |
| | | showSizeChanger: true, |
| | | showTotal: (total) => `共 ${total} 条` |
| | | }, |
| | | columns: [ |
| | | { |
| | | title: '序号', |
| | | dataIndex: '', |
| | | key: 'rowIndex', |
| | | width: 60, |
| | | align: 'center', |
| | | customRender: function (text, record, index) { |
| | | return index + 1 |
| | | } |
| | | }, |
| | | { |
| | | title: '站点名称', |
| | | align: 'center', |
| | | dataIndex: 'depart_name' |
| | | }, |
| | | { |
| | | title: '站点编码', |
| | | align: 'center', |
| | | dataIndex: 'org_code' |
| | | }, |
| | | { |
| | | title: '标签名称', |
| | | align: 'center', |
| | | dataIndex: 'label_name' |
| | | }, |
| | | { |
| | | title: '创建时间', |
| | | align: 'center', |
| | | dataIndex: 'create_time', |
| | | customRender: function (text) { |
| | | if (!text) return '' |
| | | return text.replace('T', ' ') |
| | | } |
| | | }, |
| | | { |
| | | title: '操作', |
| | | dataIndex: 'action', |
| | | align: 'center', |
| | | scopedSlots: { customRender: 'action' } |
| | | } |
| | | ], |
| | | url: { |
| | | list: '/jyz/departLabel/list', |
| | | listLabels: '/jyz/departLabel/listLabels', |
| | | listDeparts: '/jyz/custom/sys/depart/tables', |
| | | delete: '/jyz/departLabel/delete', |
| | | update: '/jyz/departLabel/update', |
| | | queryOrgOilCount: '/jyz/departLabel/queryOrgOilCount' |
| | | } |
| | | } |
| | | }, |
| | | watch: { |
| | | isContrast(newVal) { |
| | | if (newVal) { |
| | | this.setDefaultMonthTime() |
| | | } |
| | | } |
| | | }, |
| | | created() { |
| | | this.loadLabelList() |
| | | this.loadData() |
| | | }, |
| | | methods: { |
| | | loadLabelList() { |
| | | const params = { |
| | | parentCode: this.queryParam.parentCode, |
| | | parentId: this.queryParam.parentId |
| | | } |
| | | getAction(this.url.listLabels, params).then((res) => { |
| | | if (res.success) { |
| | | this.labelList = res.result |
| | | console.log('标签列表:', this.labelList) |
| | | } else { |
| | | console.error('加载标签列表失败:', res.message) |
| | | } |
| | | }) |
| | | }, |
| | | loadData() { |
| | | this.loading = true |
| | | const params = { |
| | | pageNo: this.pagination.current, |
| | | pageSize: this.pagination.pageSize, |
| | | labelName: this.queryParam.labelName, |
| | | parentCode: this.queryParam.parentCode, |
| | | parentId: this.queryParam.parentId |
| | | } |
| | | |
| | | getAction(this.url.list, params).then((res) => { |
| | | if (res.success) { |
| | | this.dataSource = res.result.records || [] |
| | | this.pagination.total = Number(res.result.total) || 0 |
| | | this.getOrgData() |
| | | } else { |
| | | this.$message.error(res.message || '查询失败') |
| | | } |
| | | }).catch(() => { |
| | | this.$message.error('查询失败') |
| | | }).finally(() => { |
| | | this.loading = false |
| | | }) |
| | | }, |
| | | updateChartData() { |
| | | if (this.dataSource.length === 0) { |
| | | this.lineChartData = {} |
| | | return |
| | | } |
| | | |
| | | console.log('updateChartData - dataSource:', this.dataSource) |
| | | console.log('updateChartData - currentOrgData:', this.currentOrgData) |
| | | console.log('updateChartData - contrastOrgData:', this.contrastOrgData) |
| | | |
| | | const xData = this.dataSource.map(item => item.depart_name) |
| | | |
| | | const currentCarData = this.dataSource.map(item => { |
| | | const orgData = this.currentOrgData.find(d => d.org_code === item.org_code) |
| | | console.log(`Matching ${item.org_code}:`, orgData) |
| | | return orgData ? (orgData.carCount || 0) : 0 |
| | | }) |
| | | |
| | | const currentOilData = this.dataSource.map(item => { |
| | | const orgData = this.currentOrgData.find(d => d.org_code === item.org_code) |
| | | return orgData ? (orgData.oilCount || 0) : 0 |
| | | }) |
| | | |
| | | const currentStationData = this.dataSource.map(item => { |
| | | const orgData = this.currentOrgData.find(d => d.org_code === item.org_code) |
| | | return orgData ? (orgData.stationCount || 0) : 0 |
| | | }) |
| | | |
| | | const currentVolumeData = this.dataSource.map(item => { |
| | | const orgData = this.currentOrgData.find(d => d.org_code === item.org_code) |
| | | return orgData ? (orgData.oilVolume || 0) : 0 |
| | | }) |
| | | |
| | | console.log('Final data - xData:', xData) |
| | | console.log('Final data - currentCarData:', currentCarData) |
| | | console.log('Final data - currentOilData:', currentOilData) |
| | | console.log('Final data - currentStationData:', currentStationData) |
| | | console.log('Final data - currentVolumeData:', currentVolumeData) |
| | | |
| | | this.lineChartData = { |
| | | xData: xData, |
| | | barData: currentCarData, |
| | | lineData: currentOilData, |
| | | barName: '车流量', |
| | | lineName: '加油数', |
| | | barData2: currentStationData, |
| | | lineData2: currentVolumeData, |
| | | barName2: '进站数', |
| | | lineName2: '油品销量' |
| | | } |
| | | |
| | | this.$nextTick(() => { |
| | | if (this.$refs.lineChart && this.$refs.lineChart.setChart) { |
| | | this.$refs.lineChart.setChart() |
| | | } |
| | | }) |
| | | }, |
| | | contrastTimeChange(e, t) { |
| | | this.contrastStartTime = t[0] |
| | | this.contrastEndTime = t[1] |
| | | this.getContrastData() |
| | | }, |
| | | setDefaultMonthTime() { |
| | | const now = new Date() |
| | | const year = now.getFullYear() |
| | | const month = now.getMonth() + 1 |
| | | const day = now.getDate() |
| | | |
| | | const startTime = `${year}-${String(month).padStart(2, '0')}-01 00:00:00` |
| | | const endTime = `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} 23:59:59` |
| | | |
| | | this.contrastStartTime = startTime |
| | | this.contrastEndTime = endTime |
| | | |
| | | this.getContrastData() |
| | | }, |
| | | getOrgData() { |
| | | postAction(this.url.queryOrgOilCount, { |
| | | orgCode: this.queryParam.parentCode |
| | | }).then((res) => { |
| | | if (res.success) { |
| | | console.log('getOrgData result:', res.result) |
| | | this.currentOrgData = res.result || [] |
| | | this.updateChartData() |
| | | } |
| | | }) |
| | | }, |
| | | getContrastData() { |
| | | postAction(this.url.queryOrgOilCount, { |
| | | orgCode: this.queryParam.parentCode, |
| | | startTime: this.contrastStartTime, |
| | | endTime: this.contrastEndTime |
| | | }).then((res) => { |
| | | if (res.success) { |
| | | console.log('getContrastData result:', res.result) |
| | | this.contrastOrgData = res.result || [] |
| | | this.updateChartData() |
| | | } |
| | | }) |
| | | }, |
| | | searchQuery() { |
| | | this.pagination.current = 1 |
| | | this.loadData() |
| | | }, |
| | | searchReset() { |
| | | this.queryParam.labelName = '' |
| | | this.pagination.current = 1 |
| | | this.loadData() |
| | | }, |
| | | handleTableChange(pagination) { |
| | | this.pagination = pagination |
| | | this.loadData() |
| | | }, |
| | | handleAdd(record) { |
| | | this.$refs.modalForm.add(record) |
| | | }, |
| | | handleDelete(record) { |
| | | this.$confirm({ |
| | | title: '确认删除', |
| | | content: '确定要删除该标签吗?', |
| | | onOk: () => { |
| | | deleteAction(this.url.delete, { |
| | | departId: record.depart_id, |
| | | labelName: record.label_name |
| | | }).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('删除成功') |
| | | this.loadData() |
| | | this.loadLabelList() |
| | | } else { |
| | | this.$message.error(res.message || '删除失败') |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | modalFormOk() { |
| | | this.loadData() |
| | | this.loadLabelList() |
| | | }, |
| | | getLabels(record) { |
| | | if (!record.label_name) return [] |
| | | return record.label_name.split(',').map(label => label.trim()).filter(label => label) |
| | | }, |
| | | handleMenuClick(record, label) { |
| | | this.$confirm({ |
| | | title: '确认删除', |
| | | content: `确定要删除"${label}"标签吗?`, |
| | | onOk: () => { |
| | | deleteAction(this.url.delete, { |
| | | departId: record.depart_id, |
| | | labelName: label |
| | | }).then((res) => { |
| | | if (res.success) { |
| | | this.$message.success('删除成功') |
| | | this.loadData() |
| | | this.loadLabelList() |
| | | } else { |
| | | this.$message.error(res.message || '删除失败') |
| | | } |
| | | }) |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped lang="less"> |
| | | .depart-label-container { |
| | | padding: 24px; |
| | | background: linear-gradient(135deg, #1a1f2e 0%, #0f1219 100%); |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .table-page-search-wrapper { |
| | | margin-bottom: 24px; |
| | | padding: 20px; |
| | | background: rgba(255, 255, 255, 0.05); |
| | | border-radius: 12px; |
| | | border: 1px solid rgba(255, 255, 255, 0.1); |
| | | backdrop-filter: blur(10px); |
| | | } |
| | | |
| | | .table-operator { |
| | | margin-bottom: 24px; |
| | | padding: 20px; |
| | | background: rgba(255, 255, 255, 0.05); |
| | | border-radius: 12px; |
| | | border: 1px solid rgba(255, 255, 255, 0.1); |
| | | backdrop-filter: blur(10px); |
| | | } |
| | | |
| | | .data-overview-ct { |
| | | .block-title { |
| | | font-size: 16px; |
| | | font-weight: 600; |
| | | color: #fff; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | } |
| | | |
| | | .chart-container { |
| | | margin: 24px 0; |
| | | padding: 24px; |
| | | background: rgba(255, 255, 255, 0.05); |
| | | border-radius: 12px; |
| | | border: 1px solid rgba(255, 255, 255, 0.1); |
| | | backdrop-filter: blur(10px); |
| | | |
| | | .chart-block { |
| | | width: 100%; |
| | | |
| | | .block-title { |
| | | font-size: 18px; |
| | | font-weight: 600; |
| | | margin-bottom: 20px; |
| | | color: #fff; |
| | | padding-bottom: 12px; |
| | | border-bottom: 2px solid rgba(91, 129, 249, 0.5); |
| | | } |
| | | } |
| | | } |
| | | |
| | | :deep(.ant-table) { |
| | | background: rgba(255, 255, 255, 0.05); |
| | | border-radius: 12px; |
| | | border: 1px solid rgba(255, 255, 255, 0.1); |
| | | |
| | | .ant-table-thead > tr > th { |
| | | background: rgba(91, 129, 249, 0.2); |
| | | color: #fff; |
| | | font-weight: 600; |
| | | border-bottom: 2px solid rgba(91, 129, 249, 0.5); |
| | | } |
| | | |
| | | .ant-table-tbody > tr > td { |
| | | background: transparent; |
| | | color: rgba(255, 255, 255, 0.85); |
| | | border-bottom: 1px solid rgba(255, 255, 255, 0.1); |
| | | } |
| | | |
| | | .ant-table-tbody > tr:hover > td { |
| | | background: rgba(91, 129, 249, 0.1); |
| | | } |
| | | } |
| | | |
| | | :deep(.ant-btn-primary) { |
| | | background: linear-gradient(135deg, #5b81f9 0%, #3d5cc9 100%); |
| | | border: none; |
| | | box-shadow: 0 4px 12px rgba(91, 129, 249, 0.3); |
| | | |
| | | &:hover { |
| | | background: linear-gradient(135deg, #6d92ff 0%, #4d6ee0 100%); |
| | | box-shadow: 0 6px 16px rgba(91, 129, 249, 0.4); |
| | | } |
| | | } |
| | | |
| | | :deep(.ant-select) { |
| | | background: rgba(255, 255, 255, 0.05); |
| | | border: 1px solid rgba(255, 255, 255, 0.2); |
| | | border-radius: 8px; |
| | | |
| | | .ant-select-selection { |
| | | background: transparent; |
| | | border: none; |
| | | color: rgba(255, 255, 255, 0.85); |
| | | } |
| | | } |
| | | |
| | | :deep(.ant-form-item-label > label) { |
| | | color: rgba(255, 255, 255, 0.85); |
| | | font-weight: 500; |
| | | } |
| | | |
| | | :deep(.a-switch) { |
| | | background: rgba(91, 129, 249, 0.3); |
| | | } |
| | | </style> |
| New file |
| | |
| | | <template> |
| | | <a-modal |
| | | :title="title" |
| | | :width="600" |
| | | :visible="visible" |
| | | :confirmLoading="confirmLoading" |
| | | @ok="handleOk" |
| | | @cancel="handleCancel" |
| | | cancelText="关闭" |
| | | > |
| | | <a-spin :spinning="confirmLoading"> |
| | | <a-form :form="form"> |
| | | <a-form-item |
| | | :labelCol="labelCol" |
| | | :wrapperCol="wrapperCol" |
| | | label="标签名称" |
| | | hasFeedback |
| | | > |
| | | <a-select |
| | | v-decorator="['labelName', { rules: [{ required: true, message: '请选择标签名称' }] }]" |
| | | placeholder="请选择标签名称" |
| | | > |
| | | <a-select-option v-for="label in labelList" :key="label" :value="label"> |
| | | {{ label }} |
| | | </a-select-option> |
| | | </a-select> |
| | | </a-form-item> |
| | | </a-form> |
| | | </a-spin> |
| | | </a-modal> |
| | | </template> |
| | | |
| | | <script> |
| | | import { postAction, getAction } from '@tievd/cube-block/lib/api/manage' |
| | | |
| | | export default { |
| | | name: 'DepartLabelModal', |
| | | props: { |
| | | parentCode: { |
| | | type: String, |
| | | default: '' |
| | | }, |
| | | parentId: { |
| | | type: String, |
| | | default: '' |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | title: '添加标签', |
| | | visible: false, |
| | | confirmLoading: false, |
| | | labelCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 5 } |
| | | }, |
| | | wrapperCol: { |
| | | xs: { span: 24 }, |
| | | sm: { span: 16 } |
| | | }, |
| | | form: this.$form.createForm(this), |
| | | url: { |
| | | add: '/jyz/departLabel/add', |
| | | listLabels: '/jyz/departLabel/listLabels' |
| | | }, |
| | | labelList: [], |
| | | currentDepartId: null |
| | | } |
| | | }, |
| | | created() { |
| | | this.loadLabelList() |
| | | }, |
| | | watch: { |
| | | parentCode() { |
| | | this.loadLabelList() |
| | | }, |
| | | parentId() { |
| | | this.loadLabelList() |
| | | } |
| | | }, |
| | | methods: { |
| | | loadLabelList() { |
| | | const params = {} |
| | | if (this.parentCode) { |
| | | params.parentCode = this.parentCode |
| | | } |
| | | if (this.parentId) { |
| | | params.parentId = this.parentId |
| | | } |
| | | getAction(this.url.listLabels, params).then((res) => { |
| | | if (res.success) { |
| | | this.labelList = res.result |
| | | } |
| | | }) |
| | | }, |
| | | add(record) { |
| | | this.form.resetFields() |
| | | this.currentDepartId = record.depart_id |
| | | this.visible = true |
| | | }, |
| | | handleOk() { |
| | | const that = this |
| | | this.form.validateFields((err, values) => { |
| | | if (!err) { |
| | | that.confirmLoading = true |
| | | const params = { |
| | | departId: that.currentDepartId, |
| | | labelName: values.labelName |
| | | } |
| | | postAction(that.url.add, params).then((res) => { |
| | | if (res.success) { |
| | | that.$message.success('添加成功') |
| | | that.$emit('ok') |
| | | that.visible = false |
| | | } else { |
| | | that.$message.error(res.message || '添加失败') |
| | | } |
| | | }).finally(() => { |
| | | that.confirmLoading = false |
| | | }) |
| | | } |
| | | }) |
| | | }, |
| | | handleCancel() { |
| | | this.visible = false |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style scoped> |
| | | </style> |
| | |
| | | }) |
| | | }, |
| | | loginSuccess() { |
| | | this.$router.push({path: '/dashboard/analysis'}).catch(() => { |
| | | this.$router.push({path: '/analysisScreen/operationBigdata'}).catch(() => { |
| | | console.log('登录跳转首页出错,这个错误从哪里来的') |
| | | }) |
| | | this.$notification.success({ |