Commit d3f64985 by lijinqi

Merge remote-tracking branch 'origin/develop' into develop

parents 5576e65f 0c1c1ff7
import request from '@/config/axios'
export const getUsersData = async (dateType) => {
return await request.get({
url: `/index/count/getUsersData`,
params:{
dateType : dateType
}
})
}
export const getOrdersData = async(dateType) => {
return await request.get({
url:`/index/count/getOrdersData`,
params:{
dateType : dateType
}
})
}
export const getApiCallsData = async(dateType) => {
return await request.get({
url:`/index/count/getApiCallsData`,
params:{
dateType : dateType
}
})
}
export const getTopBarData = async () => {
return await request.get({
url:`/index/count/getTopBarData`,
})
}
......@@ -47,6 +47,10 @@ export interface OrderVO {
afterSaleStatus?: number | null // 售后状态
refundPrice?: number | null // 退款金额
// ========== 发票基本信息 ==========
invoiceStatus?: number | null //开票状态
invoiceUrl?: String | null //发票链接
// ========== 营销基本信息 ==========
couponId?: number | null // 优惠劵编号
couponPrice?: number | null // 优惠劵减免金额
......@@ -171,6 +175,11 @@ export const pickUpOrder = async (id: number) => {
return await request.put({ url: `/trade/order/pick-up-by-id?id=${id}` })
}
//开具发票
export const issueInvoice = async (data: any) => {
return await request.put({ url: `/trade/order/issue-invoice`, data})
}
// 订单核销
export const pickUpOrderByVerifyCode = async (pickUpVerifyCode: string) => {
return await request.put({
......
......@@ -401,6 +401,25 @@ export const DeliveryTypeEnum = {
name: '到店自提'
}
}
/**
* 开票状态枚举
*/
export const InvoiceRequestEnum = {
UNINVOICED: {
type: 0,
name: '未开票'
},
INVOICING: {
type: 1,
name: '开票中'
},
INVOICED: {
type: 2,
name: '已开票'
}
}
/**
* 交易订单 - 状态
*/
......
......@@ -185,6 +185,7 @@ export enum DICT_TYPE {
TRADE_ORDER_STATUS = 'trade_order_status', // 订单 - 状态
TRADE_ORDER_ITEM_AFTER_SALE_STATUS = 'trade_order_item_after_sale_status', // 订单项 - 售后状态
TRADE_DELIVERY_TYPE = 'trade_delivery_type', // 配送方式
TRADE_INVOICE_STATUS = 'trade_invoice_status',
BROKERAGE_ENABLED_CONDITION = 'brokerage_enabled_condition', // 分佣模式
BROKERAGE_BIND_MODE = 'brokerage_bind_mode', // 分销关系绑定模式
BROKERAGE_BANK_NAME = 'brokerage_bank_name', // 佣金提现银行
......
......@@ -6,7 +6,7 @@
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
<div class="flex items-center">
<el-avatar :src="avatar" :size="70" class="mr-16px">
<img src="@/assets/imgs/avatar.gif" alt="" />
<img src="@/assets/imgs/avatar.gif" alt="用户头像"/>
</el-avatar>
<div>
<div class="text-20px">
......@@ -18,405 +18,348 @@
</div>
</div>
</el-col>
<!-- <el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">-->
<!-- <div class="h-70px flex items-center justify-end lt-sm:mt-10px">-->
<!-- <div class="px-8px text-right">-->
<!-- <div class="mb-16px text-14px text-gray-400">{{ t('workplace.project') }}</div>-->
<!-- <CountTo-->
<!-- class="text-20px"-->
<!-- :start-val="0"-->
<!-- :end-val="totalSate.project"-->
<!-- :duration="2600"-->
<!-- />-->
<!-- </div>-->
<!-- <el-divider direction="vertical" />-->
<!-- <div class="px-8px text-right">-->
<!-- <div class="mb-16px text-14px text-gray-400">{{ t('workplace.toDo') }}</div>-->
<!-- <CountTo-->
<!-- class="text-20px"-->
<!-- :start-val="0"-->
<!-- :end-val="totalSate.todo"-->
<!-- :duration="2600"-->
<!-- />-->
<!-- </div>-->
<!-- <el-divider direction="vertical" border-style="dashed" />-->
<!-- <div class="px-8px text-right">-->
<!-- <div class="mb-16px text-14px text-gray-400">{{ t('workplace.access') }}</div>-->
<!-- <CountTo-->
<!-- class="text-20px"-->
<!-- :start-val="0"-->
<!-- :end-val="totalSate.access"-->
<!-- :duration="2600"-->
<!-- />-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-col>-->
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
<div class="h-70px flex items-center justify-end lt-sm:mt-10px">
<div class="px-8px text-right">
<div class="mb-16px text-14px text-gray-400">{{ '今日新增用户' }}</div>
<CountTo
class="text-20px"
:start-val="0"
:end-val="todayData.newUsersCount"
:duration="2600"
/>
</div>
<el-divider direction="vertical"/>
<div class="px-8px text-right">
<div class="mb-16px text-14px text-gray-400">{{ '今日订单' }}</div>
<CountTo
class="text-20px"
:start-val="0"
:end-val="todayData.newOrdersCount"
:duration="2600"
/>
</div>
<el-divider direction="vertical" border-style="dashed"/>
<div class="px-8px text-right">
<div class="mb-16px text-14px text-gray-400">{{ '今日订单金额' }}</div>
<CountTo
class="text-20px"
:start-val="0"
:end-val="todayData.newOrdersAmount"
:duration="2600"
/>
</div>
</div>
</el-col>
</el-row>
</el-skeleton>
</el-card>
</div>
<!-- <el-row class="mt-8px" :gutter="8" justify="space-between">-->
<!-- <el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24" class="mb-8px">-->
<!-- <el-card shadow="never">-->
<!-- <template #header>-->
<!-- <div class="h-3 flex justify-between">-->
<!-- <span>{{ t('workplace.project') }}</span>-->
<!-- <el-link-->
<!-- type="primary"-->
<!-- :underline="false"-->
<!-- href="https://github.com/yudaocode"-->
<!-- target="_blank"-->
<!-- >-->
<!-- {{ t('action.more') }}-->
<!-- </el-link>-->
<!-- </div>-->
<!-- </template>-->
<!-- <el-skeleton :loading="loading" animated>-->
<!-- <el-row>-->
<!-- <el-col-->
<!-- v-for="(item, index) in projects"-->
<!-- :key="`card-${index}`"-->
<!-- :xl="8"-->
<!-- :lg="8"-->
<!-- :md="8"-->
<!-- :sm="24"-->
<!-- :xs="24"-->
<!-- >-->
<!-- <el-card-->
<!-- shadow="hover"-->
<!-- class="mr-5px mt-5px cursor-pointer"-->
<!-- @click="handleProjectClick(item.message)"-->
<!-- >-->
<!-- <div class="flex items-center">-->
<!-- <Icon-->
<!-- :icon="item.icon"-->
<!-- :size="25"-->
<!-- class="mr-8px"-->
<!-- :style="{ color: item.color }"-->
<!-- />-->
<!-- <span class="text-16px">{{ item.name }}</span>-->
<!-- </div>-->
<!-- <div class="mt-12px text-12px text-gray-400">{{ t(item.message) }}</div>-->
<!-- <div class="mt-12px flex justify-between text-12px text-gray-400">-->
<!-- <span>{{ item.personal }}</span>-->
<!-- <span>{{ formatTime(item.time, 'yyyy-MM-dd') }}</span>-->
<!-- </div>-->
<!-- </el-card>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- </el-skeleton>-->
<!-- </el-card>-->
<!-- 统计图 -->
<el-row class="mt-8px" :gutter="8" justify="space-between">
<el-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24" class="mb-8px">
<el-card shadow="never" class="mt-8px">
<el-skeleton :loading="loading" animated>
<el-row :gutter="20" justify="space-between">
<!-- 用户统计图表 -->
<el-col :xl="12" :lg="12" :md="12" :sm="12" :xs="12">
<div style="display: flex; justify-content: flex-end; margin-bottom: 10px;">
<el-button
@click="changeUserDateType"
type="primary"
size="small"
>
{{ userDateType === 'd' ? '切换月数据' : '切换日数据' }}
</el-button>
</div>
<el-card shadow="hover" class="mb-8px">
<el-skeleton :loading="loading" animated>
<Echart :options="usersChartOption" :height="340"/>
</el-skeleton>
</el-card>
</el-col>
<!-- <el-card shadow="never" class="mt-8px">-->
<!-- <el-skeleton :loading="loading" animated>-->
<!-- <el-row :gutter="20" justify="space-between">-->
<!-- <el-col :xl="10" :lg="10" :md="24" :sm="24" :xs="24">-->
<!-- <el-card shadow="hover" class="mb-8px">-->
<!-- <el-skeleton :loading="loading" animated>-->
<!-- <Echart :options="pieOptionsData" :height="280" />-->
<!-- </el-skeleton>-->
<!-- </el-card>-->
<!-- </el-col>-->
<!-- <el-col :xl="14" :lg="14" :md="24" :sm="24" :xs="24">-->
<!-- <el-card shadow="hover" class="mb-8px">-->
<!-- <el-skeleton :loading="loading" animated>-->
<!-- <Echart :options="barOptionsData" :height="280" />-->
<!-- </el-skeleton>-->
<!-- </el-card>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- </el-skeleton>-->
<!-- </el-card>-->
<!-- </el-col>-->
<!-- <el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" class="mb-8px">-->
<!-- <el-card shadow="never">-->
<!-- <template #header>-->
<!-- <div class="h-3 flex justify-between">-->
<!-- <span>{{ t('workplace.shortcutOperation') }}</span>-->
<!-- </div>-->
<!-- </template>-->
<!-- <el-skeleton :loading="loading" animated>-->
<!-- <el-row>-->
<!-- <el-col v-for="item in shortcut" :key="`team-${item.name}`" :span="8" class="mb-8px">-->
<!-- <div class="flex items-center">-->
<!-- <Icon :icon="item.icon" class="mr-8px" :style="{ color: item.color }" />-->
<!-- <el-link type="default" :underline="false" @click="handleShortcutClick(item.url)">-->
<!-- {{ item.name }}-->
<!-- </el-link>-->
<!-- </div>-->
<!-- </el-col>-->
<!-- </el-row>-->
<!-- </el-skeleton>-->
<!-- </el-card>-->
<!-- <el-card shadow="never" class="mt-8px">-->
<!-- <template #header>-->
<!-- <div class="h-3 flex justify-between">-->
<!-- <span>{{ t('workplace.notice') }}</span>-->
<!-- <el-link type="primary" :underline="false">{{ t('action.more') }}</el-link>-->
<!-- </div>-->
<!-- </template>-->
<!-- <el-skeleton :loading="loading" animated>-->
<!-- <div v-for="(item, index) in notice" :key="`dynamics-${index}`">-->
<!-- <div class="flex items-center">-->
<!-- <el-avatar :src="avatar" :size="35" class="mr-16px">-->
<!-- <img src="@/assets/imgs/avatar.gif" alt="" />-->
<!-- </el-avatar>-->
<!-- <div>-->
<!-- <div class="text-14px">-->
<!-- <Highlight :keys="item.keys.map((v) => t(v))">-->
<!-- {{ item.type }} : {{ item.title }}-->
<!-- </Highlight>-->
<!-- </div>-->
<!-- <div class="mt-16px text-12px text-gray-400">-->
<!-- {{ formatTime(item.date, 'yyyy-MM-dd') }}-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
<!-- <el-divider />-->
<!-- </div>-->
<!-- </el-skeleton>-->
<!-- </el-card>-->
<!-- </el-col>-->
<!-- </el-row>-->
<el-col :xl="12" :lg="12" :md="12" :sm="12" :xs="12">
<div style="display: flex; justify-content: flex-end; margin-bottom: 10px;">
<el-button
@click="changeApiCallsDateType"
type="primary"
size="small"
>
{{ apiCallsDateType === 'd' ? '切换月数据' : '切换日数据' }}
</el-button>
</div>
<el-card shadow="hover" class="mb-8px">
<el-skeleton :loading="loading" animated>
<Echart :options="apiCallChartOptions" :height="340"/>
</el-skeleton>
</el-card>
</el-col>
<!-- 第三个图表:折线图-->
<el-col :xl="24" :lg="24" :md="24" :sm="24" :xs="24">
<div style="display: flex; justify-content: flex-end; margin-bottom: 10px;">
<el-button
@click="changeOrderDateType"
type="primary"
size="small"
>
{{ orderDateType === 'd' ? '切换月数据' : '切换日数据' }}
</el-button>
</div>
<el-card shadow="hover" class="mb-8px">
<el-skeleton :loading="loading" animated>
<Echart :options="orderChartOptions" :height="420"/>
</el-skeleton>
</el-card>
</el-col>
</el-row>
</el-skeleton>
</el-card>
</el-col>
</el-row>
</div>
</template>
<script lang="ts" setup>
import { set } from 'lodash-es'
import { EChartsOption } from 'echarts'
import { formatTime } from '@/utils'
import {ref, reactive} from 'vue'
import {EChartsOption} from 'echarts'
import { useUserStore } from '@/store/modules/user'
// import { useWatermark } from '@/hooks/web/useWatermark'
import type { WorkplaceTotal, Project, Notice, Shortcut } from './types'
import { pieOptions, barOptions } from './echarts-data'
import { useRouter } from 'vue-router'
import {useUserStore} from '@/store/modules/user'
import type {WorkplaceTotal} from './types'
import {useRouter} from 'vue-router'
import {useI18n} from 'vue-i18n' // 确保导入i18n
import * as IndexCountApi from "@/api/Home/count";
import Index from "@/views/ai/music/index/index.vue";
defineOptions({ name: 'Index' })
defineOptions({name: 'Index'})
const { t } = useI18n()
// 国际化
const {t} = useI18n()
const router = useRouter()
const userStore = useUserStore()
// const { setWatermark } = useWatermark()
const loading = ref(true)
const avatar = userStore.getUser.avatar
const username = userStore.getUser.nickname
const pieOptionsData = reactive<EChartsOption>(pieOptions) as EChartsOption
// 获取统计数
let totalSate = reactive<WorkplaceTotal>({
project: 0,
access: 0,
todo: 0
})
const getCount = async () => {
const data = {
project: 40,
access: 2340,
todo: 10
}
totalSate = Object.assign(totalSate, data)
// 切换状态(d=日,m=月)
const userDateType = ref('d');
const apiCallsDateType = ref('d');
const orderDateType = ref('d');
const changeUserDateType = () => {
userDateType.value = userDateType.value === 'd' ? 'm' : 'd';
// 调用获取图表数据的方法,传入当前类型
getUsersChartData(userDateType.value);
};
const changeApiCallsDateType = () => {
apiCallsDateType.value = apiCallsDateType.value === 'd' ? 'm' : 'd';
getApiCallsChartData(apiCallsDateType.value);
}
// 获取项目数
let projects = reactive<Project[]>([])
const getProject = async () => {
const data = [
{
name: 'ruoyi-vue-pro',
icon: 'simple-icons:springboot',
message: 'github.com/YunaiV/ruoyi-vue-pro',
personal: 'Spring Boot 单体架构',
time: new Date('2025-01-02'),
color: '#6DB33F'
},
{
name: 'yudao-ui-admin-vue3',
icon: 'ep:element-plus',
message: 'github.com/yudaocode/yudao-ui-admin-vue3',
personal: 'Vue3 + element-plus 管理后台',
time: new Date('2025-02-03'),
color: '#409EFF'
},
{
name: 'yudao-ui-mall-uniapp',
icon: 'icon-park-outline:mall-bag',
message: 'github.com/yudaocode/yudao-ui-mall-uniapp',
personal: 'Vue3 + uniapp 商城手机端',
time: new Date('2025-03-04'),
color: '#ff4d4f'
},
{
name: 'yudao-cloud',
icon: 'material-symbols:cloud-outline',
message: 'github.com/YunaiV/yudao-cloud',
personal: 'Spring Cloud 微服务架构',
time: new Date('2025-04-05'),
color: '#1890ff'
},
{
name: 'yudao-ui-admin-vben',
icon: 'devicon:antdesign',
message: 'github.com/yudaocode/yudao-ui-admin-vben',
personal: 'Vue3 + vben5(antd) 管理后台',
time: new Date('2025-05-06'),
color: '#e18525'
},
const changeOrderDateType = () => {
orderDateType.value = orderDateType.value === 'd' ? 'm' : 'd';
getOrderChartData(orderDateType.value);
}
// 统计数据
const todayData = reactive<WorkplaceTotal>({
newUsersCount: 0,
newOrdersCount: 0,
newOrdersAmount: 0
})
// 用户数柱状图配置
const usersChartOption = ref<EChartsOption>({
title: {
text: '用户数',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: []
},
yAxis: {
type: 'value'
},
series: [
{
name: 'yudao-ui-admin-uniapp',
icon: 'ant-design:mobile',
message: 'github.com/yudaocode/yudao-ui-admin-uniapp',
personal: 'Vue3 + uniapp 管理手机端',
time: new Date('2025-06-01'),
color: '#2979ff'
name: t('analysis.activeQuantity'),
data: [],
type: 'bar', // 柱状图
itemStyle: {
color: '#409EFF' // 自定义颜色
}
}
]
projects = Object.assign(projects, data)
})
// 用户数柱状图数据
const getUsersChartData = async (userDateType) => {
let responseData = await IndexCountApi.getUsersData(userDateType);
// 直接修改图表配置
usersChartOption.value.xAxis!.data = responseData.map(item => t(item.countDate))
usersChartOption.value.series![0].data = responseData.map(item => item.usersCount)
}
// 获取通知公告
let notice = reactive<Notice[]>([])
const getNotice = async () => {
const data = [
{
title: '系统支持 JDK 8/17/21,Vue 2/3',
type: '技术兼容性',
keys: ['JDK', 'Vue'],
date: new Date()
},
// api调用折线图配置
const apiCallChartOptions = ref<EChartsOption>({
title: {
text: 'api调用次数',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: [] // 后续会动态填充
},
yAxis: {
type: 'value'
},
series: [
{
title: '后端提供 Spring Boot 2.7/3.2 + Cloud 双架构',
type: '架构灵活性',
keys: ['Boot', 'Cloud'],
date: new Date()
},
{
title: '全部开源,个人与企业可 100% 直接使用,无需授权',
type: '开源免授权',
keys: ['无需授权'],
date: new Date()
},
{
title: '国内使用最广泛的快速开发平台,远超 10w+ 企业使用',
type: '广泛企业认可',
keys: ['最广泛', '10w+'],
date: new Date()
name: 'API调用次数',
data: [], // 后续会动态填充
type: 'line', // 明确指定为折线图
smooth: false, // 平滑曲线
itemStyle: {
color: '#67C23A' // 自定义颜色
}
}
]
notice = Object.assign(notice, data)
}
})
// 获取快捷入口
let shortcut = reactive<Shortcut[]>([])
// api调用折线图数据
const getApiCallsChartData = async (apiCallsDateType) => {
let data = await IndexCountApi.getApiCallsData(apiCallsDateType)
// 直接修改图表配置
apiCallChartOptions.value.xAxis!.data = data.map(item => t(item.countDate))
apiCallChartOptions.value.series![0].data = data.map(item => item.callsCount)
}
const getShortcut = async () => {
const data = [
//订单折线图配置
const orderChartOptions = ref<EChartsOption>({
title: {
text: '订单相关统计',
left: 'center'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: [] // 后续会动态填充
},
yAxis: [
{
name: '首页',
icon: 'ion:home-outline',
url: '/',
color: '#1fdaca'
type: 'value',
name: '订单数量'
},
{
name: '商城中心',
icon: 'ep:shop',
url: '/mall/home',
color: '#ff6b6b'
},
// 右Y轴:金额(新增轴,index=1)
type: 'value',
name: '金额(元)', // 轴名称
position: 'right' // 放在右侧
}
],
series: [
{
name: 'AI 大模型',
icon: 'tabler:ai',
url: '/ai/chat',
color: '#7c3aed'
name: '算力订单',
data: [], // 后续会动态填充
type: 'line', // 明确指定为折线图
smooth: false, // 平滑曲线
itemStyle: {
color: '#67C23A' // 自定义颜色
}
}, {
name: 'API订单',
data: [], // 后续会动态填充
type: 'line', // 明确指定为折线图
smooth: false, // 平滑曲线
itemStyle: {
color: '#409EFF' // 自定义颜色
}
}, {
name: '总订单',
data: [], // 后续会动态填充
type: 'line', // 明确指定为折线图
smooth: false, // 平滑曲线
itemStyle: {
color: '#F59E0B' // 自定义颜色
}
},
// ---------------------- 新增3个金额系列(都绑右侧轴)----------------------
{
name: 'ERP 系统',
icon: 'simple-icons:erpnext',
url: '/erp/home',
color: '#3fb27f'
name: '算力订单金额',
data: [], // 算力金额数据
type: 'line',
smooth: false,
itemStyle: { color: '#94D274' }, // 浅绿(和算力订单数颜色呼应)
yAxisIndex: 1 // 关键:绑右侧金额轴
},
{
name: 'CRM 系统',
icon: 'simple-icons:civicrm',
url: '/crm/backlog',
color: '#4daf1bc9'
name: 'API订单金额',
data: [], // API金额数据
type: 'line',
smooth: false,
itemStyle: { color: '#7CB6FF' }, // 浅蓝(和API订单数颜色呼应)
yAxisIndex: 1 // 绑右侧金额轴
},
{
name: 'IoT 物联网',
icon: 'fa-solid:hdd',
url: '/iot/home',
color: '#1a73e8'
name: '总金额',
data: [], // 总金额数据
type: 'line',
smooth: false,
itemStyle: { color: '#FFC53D' }, // 浅黄(和总订单数颜色呼应)
yAxisIndex: 1 // 绑右侧金额轴
}
]
shortcut = Object.assign(shortcut, data)
}
})
// 用户来源
const getUserAccessSource = async () => {
const data = [
{ value: 335, name: 'analysis.directAccess' },
{ value: 310, name: 'analysis.mailMarketing' },
{ value: 234, name: 'analysis.allianceAdvertising' },
{ value: 135, name: 'analysis.videoAdvertising' },
{ value: 1548, name: 'analysis.searchEngines' }
]
set(
pieOptionsData,
'legend.data',
data.map((v) => t(v.name))
)
pieOptionsData!.series![0].data = data.map((v) => {
return {
name: t(v.name),
value: v.value
}
})
}
const barOptionsData = reactive<EChartsOption>(barOptions) as EChartsOption
//订单折线图数据
const getOrderChartData = async (orderDateType) => {
let ordersData = await IndexCountApi.getOrdersData(orderDateType);
// 周活跃量
const getWeeklyUserActivity = async () => {
const data = [
{ value: 13253, name: 'analysis.monday' },
{ value: 34235, name: 'analysis.tuesday' },
{ value: 26321, name: 'analysis.wednesday' },
{ value: 12340, name: 'analysis.thursday' },
{ value: 24643, name: 'analysis.friday' },
{ value: 1322, name: 'analysis.saturday' },
{ value: 1324, name: 'analysis.sunday' }
]
set(
barOptionsData,
'xAxis.data',
data.map((v) => t(v.name))
)
set(barOptionsData, 'series', [
{
name: t('analysis.activeQuantity'),
data: data.map((v) => v.value),
type: 'bar'
}
])
orderChartOptions.value.xAxis!.data = ordersData.map(item => t(item.countDate))
orderChartOptions.value.series![0].data = ordersData.map(item => item.computeOrdersCount)
orderChartOptions.value.series![1].data = ordersData.map(item => item.apiOrdersCount)
orderChartOptions.value.series![2].data = ordersData.map(item => item.totalOrdersCount)
orderChartOptions.value.series![3].data = ordersData.map(item => item.computeOrdersAmount)
orderChartOptions.value.series![4].data = ordersData.map(item => item.apiOrdersAmount)
orderChartOptions.value.series![5].data = ordersData.map(item => item.totalOrdersAmount)
}
const getAllApi = async () => {
// 初始化数据
const initData = async () => {
await Promise.all([
getCount(),
getProject(),
getNotice(),
getShortcut(),
getUserAccessSource(),
getWeeklyUserActivity()
getUsersChartData(userDateType.value),
getApiCallsChartData(apiCallsDateType.value),
getOrderChartData(orderDateType.value)
])
loading.value = false
}
const handleProjectClick = (message: string) => {
window.open(`https://${message}`, '_blank')
}
const handleShortcutClick = (url: string) => {
router.push(url)
// 获取统计数
const getCount = async () => {
// 模拟接口数据
// const data = {
// newUsersCount: 10,
// newOrdersCount: 40,
// newOrdersAmount: 2340
// }
let res = await IndexCountApi.getTopBarData()
Object.assign(todayData, res)
}
getAllApi()
initData()
</script>
export type WorkplaceTotal = {
project: number
access: number
todo: number
newUsersCount: number
newOrdersCount: number
newOrdersAmount: number
}
export type Project = {
......
......@@ -24,6 +24,9 @@
订单状态
</div>
<div :style="{ width: orderTableHeadWidthList[7] + 'px' }" class="flex justify-center">
开票状态
</div>
<div :style="{ width: orderTableHeadWidthList[8] + 'px' }" class="flex justify-center">
操作
</div>
</div>
......@@ -156,6 +159,11 @@
<dict-tag :type="DICT_TYPE.TRADE_ORDER_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column align="center" label="开票状态" width="120">
<template #default>
<dict-tag :type="DICT_TYPE.TRADE_INVOICE_STATUS" :value="scope.row.invoiceStatus" />
</template>
</el-table-column>
<el-table-column align="center" fixed="right" label="操作" width="160">
<template #default>
<slot :row="scope.row"></slot>
......@@ -236,7 +244,7 @@ const setOrderTableRef = (el: TableInstance) => {
tableHeadWidthAuto(el)
}
// 头部 col 宽度初始化
const orderTableHeadWidthList = ref([300, 150, 120, 120, 160, 120, 120, 160])
const orderTableHeadWidthList = ref([300, 150, 120, 120, 160, 120, 120, 120, 160])
// 头部宽度自适应
const tableHeadWidthAuto = (el: TableInstance) => {
const columns = el.store.states.columns.value
......
<template>
<Dialog v-model="dialogVisible" title="上传发票" width="45%">
<el-form-item label="发票地址" prop="invoiceUrl">
<UploadImg v-model="formData.invoiceUrl" />
<p class="upload-tips">
请上传 大小不超过 <span class="red-text">5MB</span> 格式为
<span class="red-text">png/jpg/jpeg</span>
的文件
</p>
</el-form-item>
<template #footer>
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
<el-button @click="dialogVisible = false">取 消</el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import * as TradeOrderApi from "@/api/mall/trade/order";
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const dialogVisible = ref(false) // 弹窗是否展示
const formRef = ref() // 表单 Ref
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
/** 提交表单 */
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
const submitForm = async () => {
// 提交请求
formLoading.value = true
try {
const data = unref(formData)
await TradeOrderApi.issueInvoice(data)
message.success(t('common.updateSuccess'))
dialogVisible.value = false
// 发送操作成功的事件
emit('success', true)
} catch (error) {
console.error(error);
}finally {
formLoading.value = false
}
}
const formData = ref({
id: undefined, // 订单编号
invoiceStatus: undefined,
invoiceUrl: ''
})
const open = async (row: TradeOrderApi.OrderVO) => {
resetForm()
// 设置数据
formData.value.id = row.id
formData.value.invoiceStatus = row.invoiceStatus
formData.value.invoiceUrl = row.invoiceUrl
dialogVisible.value = true
}
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined, // 订单编号
invoiceStatus: undefined,
invoiceUrl: ''
}
formRef.value?.resetFields()
}
</script>
<style scoped lang="scss">
</style>
<template>
<!-- 图片查看弹窗 -->
<el-dialog
v-model="dialogVisible"
title="查看发票图片"
:width="`600px`"
:close-on-click-modal="false">
<div class="flex justify-center p-4">
<img
:src="formData.invoiceUrl"
alt="发票图片"
class="max-w-full max-h-[500px] object-contain"
@error="handleImageError"
/>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import * as TradeOrderApi from "@/api/mall/trade/order";
const dialogVisible = ref(false) // 弹窗是否展示
const formData = ref({
id: undefined, // 订单编号
invoiceStatus: undefined,
invoiceUrl: ''
})
const open = async (row: TradeOrderApi.OrderVO) => {
resetForm()
// 设置数据
formData.value.id = row.id
formData.value.invoiceStatus = row.invoiceStatus
formData.value.invoiceUrl = row.invoiceUrl
dialogVisible.value = true
}
defineExpose({open}) // 提供 open 方法,用于打开弹窗
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined, // 订单编号
invoiceStatus: undefined,
invoiceUrl: ''
}
}
const handleImageError = (e) => {
e.target.src = 'https://picsum.photos/600/400?grayscale&blur=2'; // 占位图
e.target.alt = '图片加载失败';
};
</script>
<style scoped lang="scss">
</style>
......@@ -201,6 +201,23 @@
<Icon icon="ep:takeaway-box" />
发货
</el-dropdown-item>
<!-- 如果订单开票状态为【未开票】【开票中】,则展示【开票】按钮-->
<el-dropdown-item
v-if="
row.invoiceStatus === InvoiceRequestEnum.UNINVOICED.type ||
row.invoiceStatus === InvoiceRequestEnum.INVOICING.type
"
command="issueInvoice"
>
<Icon icon="ep:takeaway-box" />
开票
</el-dropdown-item>
<el-dropdown-item
v-if="row.invoiceStatus === InvoiceRequestEnum.INVOICED.type"
command="viewInvoice">
<Icon icon="ep:takeaway-box"/>
展示发票
</el-dropdown-item>
<el-dropdown-item command="remark">
<Icon icon="ep:chat-line-square" />
备注
......@@ -224,6 +241,8 @@
<!-- 各种操作的弹窗 -->
<OrderDeliveryForm ref="deliveryFormRef" @success="getList" />
<OrderUpdateRemarkForm ref="updateRemarkForm" @success="getList" />
<OrderIssueInvoiceForm ref="issueInvoiceFormRef" @success="getList" />
<OrderViewInvoiceForm ref="viewInvoiceFormRef" @success="getList" />
</template>
<script lang="ts" setup>
......@@ -234,7 +253,7 @@ import * as TradeOrderApi from '@/api/mall/trade/order'
import * as PickUpStoreApi from '@/api/mall/trade/delivery/pickUpStore'
import { DICT_TYPE, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
import * as DeliveryExpressApi from '@/api/mall/trade/delivery/express'
import { DeliveryTypeEnum, TradeOrderStatusEnum } from '@/utils/constants'
import {DeliveryTypeEnum, InvoiceRequestEnum, TradeOrderStatusEnum} from '@/utils/constants'
import { OrderTableColumn } from './components'
defineOptions({ name: 'TradeOrder' })
......@@ -327,6 +346,8 @@ const openDetail = (id: number) => {
/** 操作分发 */
const deliveryFormRef = ref()
const updateRemarkForm = ref()
const issueInvoiceFormRef = ref()
const viewInvoiceFormRef = ref()
const handleCommand = (command: string, row: TradeOrderApi.OrderVO) => {
switch (command) {
case 'remark':
......@@ -335,6 +356,12 @@ const handleCommand = (command: string, row: TradeOrderApi.OrderVO) => {
case 'delivery':
deliveryFormRef.value?.open(row)
break
case 'issueInvoice':
issueInvoiceFormRef.value?.open(row)
break
case 'viewInvoice':
viewInvoiceFormRef.value?.open(row)
break
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment