fix
This commit is contained in:
@@ -123,8 +123,8 @@
|
||||
const companyLogo = localStorage.getItem('CompanyLogo');
|
||||
const shortName = localStorage.getItem('ShortName');
|
||||
const logo = ref<any>('/assets/logo.svg');
|
||||
if (tenantName) {
|
||||
projectName.value = String(tenantName);
|
||||
if (tenantName!=null) {
|
||||
//projectName.value = String(tenantName);
|
||||
}
|
||||
if (companyLogo) {
|
||||
logo.value = companyLogo;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<template>
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 6 }"
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 8}"
|
||||
>
|
||||
<a-card class="analysis-chart-card" :bordered="false">
|
||||
<div class="ele-text-secondary ele-cell">
|
||||
@@ -13,25 +13,18 @@
|
||||
</div>
|
||||
<h1 class="analysis-chart-card-num">¥ 126,560</h1>
|
||||
<div class="analysis-chart-card-content" style="padding-top: 16px">
|
||||
<a-space size="middle">
|
||||
<span class="analysis-trend-text">
|
||||
周同比12% <caret-up-outlined class="ele-text-danger" />
|
||||
</span>
|
||||
<span class="analysis-trend-text">
|
||||
日同比11% <caret-down-outlined class="ele-text-success" />
|
||||
</span>
|
||||
</a-space>
|
||||
|
||||
</div>
|
||||
<a-divider />
|
||||
<div>日销售额 ¥12,423</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 6 }"
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 8 }"
|
||||
>
|
||||
<a-card class="analysis-chart-card" :bordered="false">
|
||||
<div class="ele-text-secondary ele-cell">
|
||||
<div class="ele-cell-content">访问量</div>
|
||||
<div class="ele-cell-content">总用户</div>
|
||||
<ele-tag color="red">日</ele-tag>
|
||||
</div>
|
||||
<h1 class="analysis-chart-card-num">8,846</h1>
|
||||
@@ -41,16 +34,16 @@
|
||||
style="height: 40px"
|
||||
/>
|
||||
<a-divider />
|
||||
<div>日访问量 1,234</div>
|
||||
<div>日新增用户 1,234</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 6 }"
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 8 }"
|
||||
>
|
||||
<a-card class="analysis-chart-card" :bordered="false">
|
||||
<div class="ele-text-secondary ele-cell">
|
||||
<div class="ele-cell-content">支付笔数</div>
|
||||
<ele-tag color="blue">月</ele-tag>
|
||||
<div class="ele-cell-content">总支付笔数</div>
|
||||
<ele-tag color="blue">日</ele-tag>
|
||||
</div>
|
||||
<h1 class="analysis-chart-card-num">6,560</h1>
|
||||
<v-chart
|
||||
@@ -59,37 +52,11 @@
|
||||
style="height: 40px"
|
||||
/>
|
||||
<a-divider />
|
||||
<div>转化率 60%</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 6 }"
|
||||
>
|
||||
<a-card class="analysis-chart-card" :bordered="false">
|
||||
<div class="ele-text-secondary ele-cell">
|
||||
<div class="ele-cell-content">活动运营效果</div>
|
||||
<ele-tag color="green">周</ele-tag>
|
||||
</div>
|
||||
<h1 class="analysis-chart-card-num">78%</h1>
|
||||
<div class="analysis-chart-card-content" style="padding-top: 16px">
|
||||
<a-progress
|
||||
:percent="78"
|
||||
:show-info="false"
|
||||
stroke-color="#13c2c2"
|
||||
status="active"
|
||||
/>
|
||||
</div>
|
||||
<a-divider />
|
||||
<a-space size="middle">
|
||||
<span class="analysis-trend-text">
|
||||
周同比12% <caret-up-outlined class="ele-text-danger" />
|
||||
</span>
|
||||
<span class="analysis-trend-text">
|
||||
日同比11% <caret-down-outlined class="ele-text-success" />
|
||||
</span>
|
||||
</a-space>
|
||||
<div>日新增订单 1,234</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
|
||||
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
|
||||
@@ -2,22 +2,7 @@
|
||||
<div class="ele-body ele-body-card">
|
||||
<statistics-card />
|
||||
<sale-card />
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive ? { lg: 16, md: 14, sm: 24, xs: 24 } : { span: 16 }
|
||||
"
|
||||
>
|
||||
<visit-hour />
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive ? { lg: 8, md: 10, sm: 24, xs: 24 } : { span: 8 }
|
||||
"
|
||||
>
|
||||
<hot-search />
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
||||
72
src/views/system/dashboard/components/hot-search.vue
Normal file
72
src/views/system/dashboard/components/hot-search.vue
Normal file
@@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<a-card :bordered="false" title="热门搜索">
|
||||
<v-chart
|
||||
ref="hotSearchChartRef"
|
||||
:option="hotSearchChartOption"
|
||||
style="height: 330px"
|
||||
/>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { use } from 'echarts/core';
|
||||
import type { EChartsCoreOption } from 'echarts/core';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { LineChart, BarChart } from 'echarts/charts';
|
||||
import { GridComponent, TooltipComponent } from 'echarts/components';
|
||||
import VChart from 'vue-echarts';
|
||||
import 'echarts-wordcloud';
|
||||
import { wordCloudColor } from 'ele-admin-pro/es';
|
||||
import { getWordCloudList } from '@/api/dashboard/analysis';
|
||||
import useEcharts from '@/utils/use-echarts';
|
||||
|
||||
use([CanvasRenderer, LineChart, BarChart, GridComponent, TooltipComponent]);
|
||||
|
||||
//
|
||||
const hotSearchChartRef = ref<InstanceType<typeof VChart> | null>(null);
|
||||
|
||||
useEcharts([hotSearchChartRef]);
|
||||
|
||||
// 词云图表配置
|
||||
const hotSearchChartOption: EChartsCoreOption = reactive({});
|
||||
|
||||
/* 获取词云数据 */
|
||||
const getWordCloudData = () => {
|
||||
getWordCloudList()
|
||||
.then((data) => {
|
||||
Object.assign(hotSearchChartOption, {
|
||||
tooltip: {
|
||||
show: true,
|
||||
confine: true,
|
||||
borderWidth: 1
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'wordCloud',
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
sizeRange: [12, 24],
|
||||
gridSize: 6,
|
||||
textStyle: {
|
||||
color: wordCloudColor
|
||||
},
|
||||
emphasis: {
|
||||
textStyle: {
|
||||
shadowBlur: 8,
|
||||
shadowColor: 'rgba(0, 0, 0, .15)'
|
||||
}
|
||||
},
|
||||
data: data
|
||||
}
|
||||
]
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
getWordCloudData();
|
||||
</script>
|
||||
248
src/views/system/dashboard/components/sale-card.vue
Normal file
248
src/views/system/dashboard/components/sale-card.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<a-card :bordered="false" :body-style="{ padding: 0 }">
|
||||
<a-tabs
|
||||
size="large"
|
||||
v-model:activeKey="saleSearch.type"
|
||||
class="monitor-card-tabs"
|
||||
@change="onSaleTypeChange"
|
||||
>
|
||||
<a-tab-pane tab="商品销售排行(全门店)" key="saleroom" />
|
||||
<a-tab-pane tab="用户消费排行(全门店)" />
|
||||
<template #rightExtra>
|
||||
<a-space
|
||||
size="middle"
|
||||
:class="[
|
||||
'analysis-tabs-extra',
|
||||
{ 'hidden-lg-and-down': styleResponsive }
|
||||
]"
|
||||
>
|
||||
<a-radio-group v-model:value="saleSearch.dateType">
|
||||
<a-radio-button value="1">今天</a-radio-button>
|
||||
<a-radio-button value="2">本周</a-radio-button>
|
||||
<a-radio-button value="3">本月</a-radio-button>
|
||||
<a-radio-button value="4">本年</a-radio-button>
|
||||
</a-radio-group>
|
||||
<div style="width: 300px">
|
||||
<a-range-picker
|
||||
value-format="YYYY-MM-DD"
|
||||
v-model:value="saleSearch.datetime"
|
||||
/>
|
||||
</div>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-tabs>
|
||||
<div style="padding-bottom: 10px">
|
||||
<a-row :gutter="16">
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive ? { lg: 17, md: 15, sm: 24, xs: 24 } : { span: 17 }
|
||||
"
|
||||
>
|
||||
<div v-if="saleSearch.type === 'saleroom'" class="demo-monitor-title">
|
||||
销售量趋势
|
||||
</div>
|
||||
<div v-else class="demo-monitor-title">访问量趋势</div>
|
||||
<v-chart
|
||||
ref="saleChartRef"
|
||||
:option="saleChartOption"
|
||||
style="height: 320px"
|
||||
/>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="
|
||||
styleResponsive ? { lg: 7, md: 9, sm: 24, xs: 24 } : { span: 7 }
|
||||
"
|
||||
>
|
||||
<div v-if="saleSearch.type === 'saleroom'">
|
||||
<div class="demo-monitor-title">门店销售额排名</div>
|
||||
<div
|
||||
v-for="(item, index) in saleroomRankData"
|
||||
:key="index"
|
||||
class="demo-monitor-rank-item ele-cell"
|
||||
>
|
||||
<ele-tag
|
||||
shape="circle"
|
||||
:color="index < 3 ? '#314659' : ''"
|
||||
style="border: none"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</ele-tag>
|
||||
<div class="ele-cell-content ele-elip">{{ item.name }}</div>
|
||||
<div class="ele-text-secondary">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else>
|
||||
<div class="demo-monitor-title">门店访问量排名</div>
|
||||
<div
|
||||
v-for="(item, index) in visitsRankData"
|
||||
:key="index"
|
||||
class="demo-monitor-rank-item ele-cell"
|
||||
>
|
||||
<ele-tag
|
||||
shape="circle"
|
||||
:color="index < 3 ? '#314659' : ''"
|
||||
style="border: none"
|
||||
>
|
||||
{{ index + 1 }}
|
||||
</ele-tag>
|
||||
<div class="ele-cell-content ele-elip">{{ item.name }}</div>
|
||||
<div class="ele-text-secondary">{{ item.value }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</div>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { use } from 'echarts/core';
|
||||
import type { EChartsCoreOption } from 'echarts/core';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { BarChart } from 'echarts/charts';
|
||||
import { GridComponent, TooltipComponent } from 'echarts/components';
|
||||
import VChart from 'vue-echarts';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { getSaleroomList } from '@/api/dashboard/analysis';
|
||||
import type { SaleroomData } from '@/api/dashboard/analysis/model';
|
||||
import useEcharts from '@/utils/use-echarts';
|
||||
|
||||
use([CanvasRenderer, BarChart, GridComponent, TooltipComponent]);
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
//
|
||||
const saleChartRef = ref<InstanceType<typeof VChart> | null>(null);
|
||||
|
||||
useEcharts([saleChartRef]);
|
||||
|
||||
// 销售额柱状图配置
|
||||
const saleChartOption: EChartsCoreOption = reactive({});
|
||||
|
||||
// 门店销售排名数据
|
||||
const saleroomRankData = ref([
|
||||
{ name: '工专路 1 号店', value: '323,234' },
|
||||
{ name: '工专路 2 号店', value: '323,234' },
|
||||
{ name: '工专路 3 号店', value: '323,234' },
|
||||
{ name: '工专路 4 号店', value: '323,234' },
|
||||
{ name: '工专路 5 号店', value: '323,234' },
|
||||
{ name: '工专路 6 号店', value: '323,234' },
|
||||
{ name: '工专路 7 号店', value: '323,234' }
|
||||
]);
|
||||
|
||||
// 门店访问排名数据
|
||||
const visitsRankData = ref([
|
||||
{ name: '工专路 1 号店', value: '323,234' },
|
||||
{ name: '工专路 2 号店', value: '323,234' },
|
||||
{ name: '工专路 3 号店', value: '323,234' },
|
||||
{ name: '工专路 4 号店', value: '323,234' },
|
||||
{ name: '工专路 5 号店', value: '323,234' },
|
||||
{ name: '工专路 6 号店', value: '323,234' },
|
||||
{ name: '工专路 7 号店', value: '323,234' }
|
||||
]);
|
||||
|
||||
// 销售量趋势数据
|
||||
const saleroomData1 = ref<SaleroomData[]>([]);
|
||||
|
||||
// 访问量趋势数据
|
||||
const saleroomData2 = ref<SaleroomData[]>([]);
|
||||
|
||||
interface SaleSearchType {
|
||||
type: string;
|
||||
dateType: string;
|
||||
datetime: [string, string];
|
||||
}
|
||||
|
||||
// 销售量搜索参数
|
||||
const saleSearch = reactive<SaleSearchType>({
|
||||
type: 'saleroom',
|
||||
dateType: '1',
|
||||
datetime: ['2022-01-08', '2022-02-12']
|
||||
});
|
||||
|
||||
/* 获取销售量数据 */
|
||||
const getSaleroomData = () => {
|
||||
getSaleroomList()
|
||||
.then((data) => {
|
||||
saleroomData1.value = data.list1;
|
||||
saleroomData2.value = data.list2;
|
||||
onSaleTypeChange();
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
/* 销售量tab选择改变事件 */
|
||||
const onSaleTypeChange = () => {
|
||||
if (saleSearch.type === 'saleroom') {
|
||||
Object.assign(saleChartOption, {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: saleroomData1.value.map((d) => d.month)
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: saleroomData1.value.map((d) => d.value)
|
||||
}
|
||||
]
|
||||
});
|
||||
} else {
|
||||
Object.assign(saleChartOption, {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: saleroomData2.value.map((d) => d.month)
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: saleroomData2.value.map((d) => d.value)
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
getSaleroomData();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.monitor-card-tabs :deep(.ant-tabs-nav) {
|
||||
padding: 0 16px;
|
||||
}
|
||||
|
||||
.demo-monitor-title {
|
||||
padding: 6px 20px;
|
||||
}
|
||||
|
||||
.demo-monitor-rank-item {
|
||||
padding: 0 20px;
|
||||
margin-top: 18px;
|
||||
}
|
||||
</style>
|
||||
236
src/views/system/dashboard/components/statistics-card.vue
Normal file
236
src/views/system/dashboard/components/statistics-card.vue
Normal file
@@ -0,0 +1,236 @@
|
||||
<!-- 统计卡片 -->
|
||||
<template>
|
||||
<a-row :gutter="16" v-if="data">
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 6 }"
|
||||
>
|
||||
<a-card class="analysis-chart-card" :bordered="false">
|
||||
<div class="ele-text-secondary ele-cell">
|
||||
<div class="ele-cell-content">总销售额</div>
|
||||
<a-tooltip title="指标说明">
|
||||
<ele-tag color="green">全门店</ele-tag>
|
||||
</a-tooltip>
|
||||
</div>
|
||||
<h1 class="analysis-chart-card-num"
|
||||
>¥ {{ formatNumber(data[0].totalPrice) }}</h1
|
||||
>
|
||||
<a-divider />
|
||||
<div class="flex justify-between">
|
||||
<span>本月订单数</span>
|
||||
<span>{{ data.todayOrders }}单</span>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 6 }"
|
||||
>
|
||||
<a-card class="analysis-chart-card" :bordered="false">
|
||||
<div class="ele-text-secondary ele-cell">
|
||||
<div class="ele-cell-content">订单总数</div>
|
||||
<ele-tag color="green">全门店</ele-tag>
|
||||
</div>
|
||||
<h1 class="analysis-chart-card-num">{{ data.totalOrders }}</h1>
|
||||
<a-divider />
|
||||
<div class="flex justify-between">
|
||||
<span>昨日订单数</span>
|
||||
<span>{{ 634 }}单</span>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 6 }"
|
||||
>
|
||||
<a-card class="analysis-chart-card" :bordered="false">
|
||||
<div class="ele-text-secondary ele-cell">
|
||||
<div class="ele-cell-content">会员总数</div>
|
||||
<ele-tag color="blue">全门店</ele-tag>
|
||||
</div>
|
||||
<h1 class="analysis-chart-card-num">16,560</h1>
|
||||
<a-divider />
|
||||
<div class="flex justify-between">
|
||||
<span>今日订单数</span>
|
||||
<span>1单</span>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
<a-col
|
||||
v-bind="styleResponsive ? { lg: 6, md: 12, sm: 24, xs: 24 } : { span: 6 }"
|
||||
>
|
||||
<a-card class="analysis-chart-card" :bordered="false">
|
||||
<div class="ele-text-secondary ele-cell">
|
||||
<div class="ele-cell-content">商品总数</div>
|
||||
<ele-tag color="green">全门店</ele-tag>
|
||||
</div>
|
||||
<h1 class="analysis-chart-card-num">78</h1>
|
||||
<!-- <div class="analysis-chart-card-content" style="padding-top: 16px">-->
|
||||
<!-- </div>-->
|
||||
<a-divider />
|
||||
<div class="flex justify-between">
|
||||
<span>今日订单数</span>
|
||||
<span>1单</span>
|
||||
</div>
|
||||
</a-card>
|
||||
</a-col>
|
||||
</a-row>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import {
|
||||
QuestionCircleOutlined,
|
||||
CaretUpOutlined,
|
||||
CaretDownOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
import { use } from 'echarts/core';
|
||||
import type { EChartsCoreOption } from 'echarts/core';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { LineChart, BarChart } from 'echarts/charts';
|
||||
import { GridComponent, TooltipComponent } from 'echarts/components';
|
||||
import VChart from 'vue-echarts';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { formatNumber } from 'ele-admin-pro/es';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
import { getPayNumList } from '@/api/dashboard/analysis';
|
||||
import useEcharts from '@/utils/use-echarts';
|
||||
import { Count } from '@/api/shop/count/model';
|
||||
|
||||
use([CanvasRenderer, LineChart, BarChart, GridComponent, TooltipComponent]);
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Count | null;
|
||||
}>();
|
||||
|
||||
//
|
||||
const visitChartRef = ref<InstanceType<typeof VChart> | null>(null);
|
||||
const payNumChartRef = ref<InstanceType<typeof VChart> | null>(null);
|
||||
|
||||
useEcharts([visitChartRef, payNumChartRef]);
|
||||
|
||||
// 访问量折线图配置
|
||||
const visitChartOption: EChartsCoreOption = reactive({});
|
||||
|
||||
// 支付笔数柱状图配置
|
||||
const payNumChartOption: EChartsCoreOption = reactive({});
|
||||
|
||||
/* 获取支付笔数数据 */
|
||||
const getPayNumData = () => {
|
||||
getPayNumList()
|
||||
.then((data) => {
|
||||
Object.assign(visitChartOption, {
|
||||
color: '#975fe5',
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter:
|
||||
'<i class="ele-chart-dot" style="background: #975fe5;"></i>{b0}: {c0}'
|
||||
},
|
||||
grid: {
|
||||
top: 10,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
show: false,
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: data.map((d) => d.date)
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
show: false,
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
areaStyle: {
|
||||
opacity: 0.5
|
||||
},
|
||||
data: data.map((d) => d.value)
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
Object.assign(payNumChartOption, {
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
formatter:
|
||||
'<i class="ele-chart-dot" style="background: #5b8ff9;"></i>{b0}: {c0}'
|
||||
},
|
||||
grid: {
|
||||
top: 10,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
right: 0
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
show: false,
|
||||
type: 'category',
|
||||
data: data.map((d) => d.date)
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
show: false,
|
||||
type: 'value',
|
||||
splitLine: {
|
||||
show: false
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
data: data.map((d) => d.value)
|
||||
}
|
||||
]
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
getPayNumData();
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.analysis-chart-card {
|
||||
:deep(.ant-card-body) {
|
||||
padding: 16px 22px 12px 22px;
|
||||
}
|
||||
|
||||
:deep(.ant-divider) {
|
||||
margin: 12px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.analysis-chart-card-num {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.analysis-chart-card-content {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.analysis-trend-text {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
101
src/views/system/dashboard/components/visit-hour.vue
Normal file
101
src/views/system/dashboard/components/visit-hour.vue
Normal file
@@ -0,0 +1,101 @@
|
||||
<template>
|
||||
<a-card
|
||||
:bordered="false"
|
||||
title="最近1小时访问情况"
|
||||
:body-style="{ padding: '16px 6px 0 0' }"
|
||||
>
|
||||
<v-chart
|
||||
ref="visitHourChartRef"
|
||||
:option="visitHourChartOption"
|
||||
style="height: 362px"
|
||||
/>
|
||||
</a-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, reactive } from 'vue';
|
||||
import { message } from 'ant-design-vue/es';
|
||||
import { use } from 'echarts/core';
|
||||
import type { EChartsCoreOption } from 'echarts/core';
|
||||
import { CanvasRenderer } from 'echarts/renderers';
|
||||
import { LineChart } from 'echarts/charts';
|
||||
import {
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent
|
||||
} from 'echarts/components';
|
||||
import VChart from 'vue-echarts';
|
||||
import { getVisitHourList } from '@/api/dashboard/analysis';
|
||||
import useEcharts from '@/utils/use-echarts';
|
||||
|
||||
use([
|
||||
CanvasRenderer,
|
||||
LineChart,
|
||||
GridComponent,
|
||||
TooltipComponent,
|
||||
LegendComponent
|
||||
]);
|
||||
|
||||
//
|
||||
const visitHourChartRef = ref<InstanceType<typeof VChart> | null>(null);
|
||||
|
||||
useEcharts([visitHourChartRef]);
|
||||
|
||||
// 最近 1 小时访问情况折线图配置
|
||||
const visitHourChartOption: EChartsCoreOption = reactive({});
|
||||
|
||||
/* 获取最近 1 小时访问情况数据 */
|
||||
const getVisitHourData = () => {
|
||||
getVisitHourList()
|
||||
.then((data) => {
|
||||
Object.assign(visitHourChartOption, {
|
||||
tooltip: {
|
||||
trigger: 'axis'
|
||||
},
|
||||
legend: {
|
||||
data: ['浏览量', '访问量'],
|
||||
right: 20
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: data.map((d) => d.time)
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value'
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '浏览量',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
areaStyle: {
|
||||
opacity: 0.5
|
||||
},
|
||||
data: data.map((d) => d.views)
|
||||
},
|
||||
{
|
||||
name: '访问量',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
symbol: 'none',
|
||||
areaStyle: {
|
||||
opacity: 0.5
|
||||
},
|
||||
data: data.map((d) => d.visits)
|
||||
}
|
||||
]
|
||||
});
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
getVisitHourData();
|
||||
</script>
|
||||
39
src/views/system/dashboard/index.vue
Normal file
39
src/views/system/dashboard/index.vue
Normal file
@@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<div class="ele-body ele-body-card">
|
||||
<statistics-card :data="statistics" />
|
||||
<sale-card />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from 'vue';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { useThemeStore } from '@/store/modules/theme';
|
||||
//import * as ShopCountApi from '@/api/shop/count';
|
||||
import StatisticsCard from './components/statistics-card.vue';
|
||||
import SaleCard from './components/sale-card.vue';
|
||||
import VisitHour from './components/visit-hour.vue';
|
||||
import HotSearch from './components/hot-search.vue';
|
||||
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const { styleResponsive } = storeToRefs(themeStore);
|
||||
|
||||
const statistics = ref();
|
||||
|
||||
const reload = () => {
|
||||
// ShopCountApi.data({}).then((list) => {
|
||||
// if (list) {
|
||||
// statistics.value = list;
|
||||
// }
|
||||
// });
|
||||
};
|
||||
|
||||
reload();
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
export default {
|
||||
name: 'DashboardAnalysis'
|
||||
};
|
||||
</script>
|
||||
@@ -304,7 +304,18 @@
|
||||
<template v-if="column.key === 'expirationDay'">
|
||||
<span class="ele-text-danger">{{ expirationDay(record) }}</span>
|
||||
</template>
|
||||
|
||||
<template v-if="column.key === 'action'">
|
||||
|
||||
<view >
|
||||
<a-button
|
||||
class="ele-text-danger"
|
||||
@click="openOrderOnline(record)"
|
||||
>退租</a-button>
|
||||
</view>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</a-table>
|
||||
</a-spin>
|
||||
</a-card>
|
||||
@@ -403,6 +414,11 @@
|
||||
</a-col>
|
||||
</a-row>
|
||||
</a-card>
|
||||
<order-online
|
||||
v-model:visible="showOrderOnline"
|
||||
|
||||
/>
|
||||
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
@@ -415,6 +431,7 @@ import {formatNumber} from 'ele-admin-pro/es';
|
||||
import {storeToRefs} from 'pinia';
|
||||
import {copyText} from '@/utils/common';
|
||||
import {Order} from '@/api/order/model';
|
||||
import OrderOnline from '../components/order-online.vue';
|
||||
import {listEquipmentOrderGoods} from '@/api/apps/equipment/order/goods';
|
||||
import {EquipmentOrderGoods} from '@/api/apps/equipment/order/goods/model';
|
||||
import * as EquipmentApi from '@/api/apps/equipment';
|
||||
@@ -423,12 +440,17 @@ import {ColumnItem, DatasourceFunction} from 'ele-admin-pro/es/ele-pro-table/typ
|
||||
import {listOrder, listOrderPay} from '@/api/order';
|
||||
import {CopyOutlined} from '@ant-design/icons-vue';
|
||||
import {EquipmentRecord} from '@/api/apps/equipment/record/model';
|
||||
import OrderRefund from "@/views/yunxinwei/order/components/order-refund.vue";
|
||||
|
||||
const useForm = Form.useForm;
|
||||
// 是否开启响应式布局
|
||||
const themeStore = useThemeStore();
|
||||
const {styleResponsive} = storeToRefs(themeStore);
|
||||
|
||||
const showOrderOnline = ref(false);
|
||||
// 当前编辑数据
|
||||
const current = ref<Order | null>(null);
|
||||
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
@@ -600,7 +622,15 @@ const columns2 = ref<ColumnItem[]>([
|
||||
title: '逾期状态',
|
||||
dataIndex: 'expirationDay',
|
||||
key: 'expirationDay'
|
||||
}
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
align: 'center',
|
||||
fixed: 'left',
|
||||
hideInSetting: true
|
||||
},
|
||||
]);
|
||||
const columns3 = ref<ColumnItem[]>([
|
||||
{
|
||||
@@ -702,6 +732,13 @@ const getEquipmentOrderGoods = () => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/* 线下缴费 */
|
||||
const openOrderOnline = (row?: Order) => {
|
||||
current.value = row ?? null;
|
||||
showOrderOnline.value = true;
|
||||
};
|
||||
|
||||
const getEquipment = () => {
|
||||
EquipmentApi.listEquipment({orderId: order.orderId}).then((data) => {
|
||||
if (data.length > 0) {
|
||||
|
||||
186
src/views/yunxinwei/order/components/order-online.vue
Normal file
186
src/views/yunxinwei/order/components/order-online.vue
Normal file
@@ -0,0 +1,186 @@
|
||||
<!-- 用户编辑弹窗 -->
|
||||
<template>
|
||||
<ele-modal
|
||||
:width="500"
|
||||
:visible="visible"
|
||||
:confirm-loading="loading"
|
||||
:maskClosable="false"
|
||||
:maxable="maxable"
|
||||
:title="isUpdate ? '发货' : '发货'"
|
||||
:body-style="{ paddingBottom: '8px' }"
|
||||
@update:visible="updateVisible"
|
||||
@ok="save"
|
||||
>
|
||||
<a-space>
|
||||
<a-form>
|
||||
<a-form-item label="设备编码" v-bind="validateInfos.equipmentCode">
|
||||
<a-input
|
||||
allow-clear
|
||||
:maxlength="30"
|
||||
placeholder="请输入设备编码"
|
||||
v-model:value="form.equipmentCode"
|
||||
@blur="
|
||||
validate('equipmentCode', { trigger: 'blur' }).catch(() => {})
|
||||
"
|
||||
/>
|
||||
</a-form-item>
|
||||
<!-- <a-row :gutter="16">-->
|
||||
<!-- <a-col :md="12" :sm="24" :xs="24">-->
|
||||
<!-- <a-form-item label="选择设备" v-bind="validateInfos.customerName">-->
|
||||
<!-- <a-input-->
|
||||
<!-- allow-clear-->
|
||||
<!-- :maxlength="30"-->
|
||||
<!-- placeholder="请选择设备"-->
|
||||
<!-- v-model:value="form.customerName"-->
|
||||
<!-- @blur="-->
|
||||
<!-- validate('customerName', { trigger: 'blur' }).catch(() => {})-->
|
||||
<!-- "-->
|
||||
<!-- />-->
|
||||
<!-- </a-form-item>-->
|
||||
<!-- </a-col>-->
|
||||
<!-- <a-col :md="12" :sm="24" :xs="24">-->
|
||||
<!-- <a-form-item label="手机号码" v-bind="validateInfos.customerMobile">-->
|
||||
<!-- <a-input-->
|
||||
<!-- allow-clear-->
|
||||
<!-- :maxlength="20"-->
|
||||
<!-- placeholder="请填写联系人手机号码"-->
|
||||
<!-- v-model:value="form.customerMobile"-->
|
||||
<!-- />-->
|
||||
<!-- </a-form-item>-->
|
||||
<!-- </a-col>-->
|
||||
<!-- </a-row>-->
|
||||
</a-form>
|
||||
</a-space>
|
||||
</ele-modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import {ref, reactive, watch, computed} from 'vue';
|
||||
import { Form, message } from 'ant-design-vue';
|
||||
import { assignObject } from 'ele-admin-pro';
|
||||
import TypeSelect from './customer-edit/type-select.vue';
|
||||
import ProgressSelect from './customer-edit/progress-select.vue';
|
||||
import SourceSelect from './customer-edit/source-select.vue';
|
||||
import { bindEquipment } from '@/api/apps/equipment';
|
||||
import type { Customer } from '@/api/oa/customer/model';
|
||||
import { createCode } from '@/utils/common';
|
||||
import { uploadFile } from '@/api/system/file';
|
||||
import type { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
|
||||
import { FILE_SERVER } from '@/config/setting';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { Equipment } from "@/api/apps/equipment/model";
|
||||
import { Order } from "@/api/order/model";
|
||||
|
||||
const userStore = useUserStore();
|
||||
// 当前用户信息
|
||||
const loginUser = computed(() => userStore.info ?? {});
|
||||
// 是否是修改
|
||||
const isUpdate = ref(false);
|
||||
const useForm = Form.useForm;
|
||||
const props = defineProps<{
|
||||
// 弹窗是否打开
|
||||
visible: boolean;
|
||||
// 修改回显的数据
|
||||
data?: Order | null;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'done'): void;
|
||||
(e: 'update:visible', visible: boolean): void;
|
||||
}>();
|
||||
|
||||
// 提交状态
|
||||
const loading = ref(false);
|
||||
// 是否显示最大化切换按钮
|
||||
const maxable = ref(true);
|
||||
|
||||
// 表单数据
|
||||
const form = reactive<Equipment>({
|
||||
equipmentCode: '',
|
||||
orderId: undefined
|
||||
});
|
||||
|
||||
// 已上传数据, 可赋初始值用于回显
|
||||
const images = ref(<any>[]);
|
||||
|
||||
/* 更新visible */
|
||||
const updateVisible = (value: boolean) => {
|
||||
emit('update:visible', value);
|
||||
};
|
||||
|
||||
// 表单验证规则
|
||||
const rules = reactive({
|
||||
equipmentCode: [
|
||||
{
|
||||
required: true,
|
||||
type: 'string',
|
||||
message: '请输入设备编码',
|
||||
trigger: 'blur'
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
const { resetFields, validate, validateInfos } = useForm(form, rules);
|
||||
|
||||
/* 保存编辑 */
|
||||
const save = () => {
|
||||
validate()
|
||||
.then(() => {
|
||||
loading.value = true;
|
||||
// 去除空格
|
||||
const data = {
|
||||
...form,
|
||||
orderId: props.data?.orderId,
|
||||
userId: props.data?.userId
|
||||
};
|
||||
// 转字符串
|
||||
bindEquipment(data)
|
||||
.then((msg) => {
|
||||
loading.value = false;
|
||||
message.success(msg);
|
||||
updateVisible(false);
|
||||
emit('done');
|
||||
})
|
||||
.catch((e) => {
|
||||
loading.value = false;
|
||||
message.error(e.message);
|
||||
});
|
||||
})
|
||||
.catch(() => {});
|
||||
};
|
||||
|
||||
const onUpload = (d: ItemType) => {
|
||||
uploadFile(<File>d.file)
|
||||
.then((result) => {
|
||||
form.customerAvatar = result.path;
|
||||
message.success('上传成功');
|
||||
})
|
||||
.catch((e) => {
|
||||
message.error(e.message);
|
||||
});
|
||||
};
|
||||
|
||||
watch(
|
||||
() => props.visible,
|
||||
(visible) => {
|
||||
if (visible) {
|
||||
if (props.data) {
|
||||
loading.value = false;
|
||||
// 头像赋值
|
||||
images.value = [];
|
||||
if(props.data.customerAvatar){
|
||||
images.value.push({ uid:1, url: FILE_SERVER + props.data.customerAvatar, status: '' });
|
||||
}
|
||||
assignObject(form, props.data);
|
||||
isUpdate.value = true;
|
||||
} else {
|
||||
form.customerCode = createCode();
|
||||
isUpdate.value = false;
|
||||
}
|
||||
} else {
|
||||
resetFields();
|
||||
}
|
||||
}
|
||||
);
|
||||
</script>
|
||||
<style lang="less"></style>
|
||||
Reference in New Issue
Block a user