This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* H5 运营弹窗配置相关 API
|
* H5 运营弹窗配置相关 API
|
||||||
* 契约来源:docs/产品迭代8月份/通知.md
|
* 契约来源:docs/产品迭代8月份/h5.md
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { BaseService } from '../BaseService'
|
import { BaseService } from '../BaseService'
|
||||||
|
|||||||
@@ -1,5 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<ElDialog v-model="visible" title="凭证查看" width="560px" @closed="handleClose">
|
<div v-if="inline" class="voucher-preview voucher-preview--inline">
|
||||||
|
<div v-if="loading" class="voucher-preview__loading">加载中...</div>
|
||||||
|
<div v-else-if="files.length" class="voucher-preview__list">
|
||||||
|
<div v-for="file in files" :key="file.key" class="voucher-preview__item">
|
||||||
|
<ElImage
|
||||||
|
v-if="file.isImage"
|
||||||
|
class="voucher-preview__image"
|
||||||
|
:src="file.url"
|
||||||
|
:preview-src-list="imageUrls"
|
||||||
|
:initial-index="getImageIndex(file.url)"
|
||||||
|
fit="cover"
|
||||||
|
preview-teleported
|
||||||
|
/>
|
||||||
|
<div v-else class="voucher-preview__file">
|
||||||
|
<ElIcon><component :is="getFileIcon(file)" /></ElIcon>
|
||||||
|
<span class="voucher-preview__file-type">{{ file.fileType }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="voucher-preview__meta">
|
||||||
|
<div class="voucher-preview__name" :title="file.name">{{
|
||||||
|
getDisplayName(file.name)
|
||||||
|
}}</div>
|
||||||
|
<ElButton link type="primary" @click="openFile(file)">查看/下载</ElButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<ElEmpty v-else description="暂无凭证" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ElDialog v-else v-model="visible" title="凭证查看" width="560px" @closed="handleClose">
|
||||||
<div v-if="loading" class="voucher-preview__loading">加载中...</div>
|
<div v-if="loading" class="voucher-preview__loading">加载中...</div>
|
||||||
<div v-else-if="files.length" class="voucher-preview__list">
|
<div v-else-if="files.length" class="voucher-preview__list">
|
||||||
<div v-for="file in files" :key="file.key" class="voucher-preview__item">
|
<div v-for="file in files" :key="file.key" class="voucher-preview__item">
|
||||||
@@ -48,6 +76,7 @@
|
|||||||
interface Props {
|
interface Props {
|
||||||
fileKey?: string | string[]
|
fileKey?: string | string[]
|
||||||
fileKeys?: string[]
|
fileKeys?: string[]
|
||||||
|
inline?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VoucherFile {
|
interface VoucherFile {
|
||||||
@@ -59,7 +88,9 @@
|
|||||||
fileType: string
|
fileType: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
inline: false
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
close: []
|
close: []
|
||||||
@@ -165,7 +196,7 @@
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
visible.value = true
|
visible.value = !props.inline
|
||||||
loading.value = true
|
loading.value = true
|
||||||
files.value = []
|
files.value = []
|
||||||
|
|
||||||
|
|||||||
@@ -525,6 +525,7 @@
|
|||||||
"detailsOfPaymentMerchant": "Payment Merchant Details",
|
"detailsOfPaymentMerchant": "Payment Merchant Details",
|
||||||
"detailsOfPaymentMerchantPool": "Merchant Pool Details",
|
"detailsOfPaymentMerchantPool": "Merchant Pool Details",
|
||||||
"h5PopupConfiguration": "H5 Popup Configurations",
|
"h5PopupConfiguration": "H5 Popup Configurations",
|
||||||
|
"h5PopupConfigurationDetail": "H5 Popup Configuration Detail",
|
||||||
"assetWalletAutoRenewal": "Asset Wallet Auto Renewal"
|
"assetWalletAutoRenewal": "Asset Wallet Auto Renewal"
|
||||||
},
|
},
|
||||||
"batch": {
|
"batch": {
|
||||||
|
|||||||
@@ -476,6 +476,7 @@
|
|||||||
"detailsOfPaymentMerchant": "支付商户详情",
|
"detailsOfPaymentMerchant": "支付商户详情",
|
||||||
"detailsOfPaymentMerchantPool": "商户池详情",
|
"detailsOfPaymentMerchantPool": "商户池详情",
|
||||||
"h5PopupConfiguration": "H5运营弹窗配置",
|
"h5PopupConfiguration": "H5运营弹窗配置",
|
||||||
|
"h5PopupConfigurationDetail": "H5运营弹窗配置详情",
|
||||||
"assetWalletAutoRenewal": "资产钱包自动续费"
|
"assetWalletAutoRenewal": "资产钱包自动续费"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -430,29 +430,6 @@ export const asyncRoutes: AppRouteRecord[] = [
|
|||||||
keepAlive: true
|
keepAlive: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 解绑导入任务列表
|
|
||||||
{
|
|
||||||
path: 'phone-asset-association/unbind-import-tasks',
|
|
||||||
name: 'PhoneAssetUnbindImportTasks',
|
|
||||||
component: RoutesAlias.PhoneAssetUnbindImportTasks,
|
|
||||||
meta: {
|
|
||||||
title: 'menus.assetManagement.phoneAssetUnbindImportTasks',
|
|
||||||
permissions: [AUGUST_PERMISSIONS.phoneAssetAssociation.unbindImportPage],
|
|
||||||
keepAlive: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 解绑导入任务详情
|
|
||||||
{
|
|
||||||
path: 'phone-asset-association/unbind-import-tasks/detail',
|
|
||||||
name: 'PhoneAssetUnbindImportTaskDetail',
|
|
||||||
component: RoutesAlias.PhoneAssetUnbindImportTaskDetail,
|
|
||||||
meta: {
|
|
||||||
title: 'menus.assetManagement.phoneAssetUnbindImportTaskDetail',
|
|
||||||
permissions: [AUGUST_PERMISSIONS.phoneAssetAssociation.unbindImportDetail],
|
|
||||||
isHide: true,
|
|
||||||
keepAlive: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// 记录管理
|
// 记录管理
|
||||||
{
|
{
|
||||||
path: 'record-management',
|
path: 'record-management',
|
||||||
@@ -557,6 +534,29 @@ export const asyncRoutes: AppRouteRecord[] = [
|
|||||||
title: 'menus.assetManagement.orderPackageInvalidateTask',
|
title: 'menus.assetManagement.orderPackageInvalidateTask',
|
||||||
keepAlive: true
|
keepAlive: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
// 解绑导入任务
|
||||||
|
{
|
||||||
|
path: 'phone-asset-unbind-import-tasks',
|
||||||
|
name: 'PhoneAssetUnbindImportTasks',
|
||||||
|
component: RoutesAlias.PhoneAssetUnbindImportTasks,
|
||||||
|
meta: {
|
||||||
|
title: 'menus.assetManagement.phoneAssetUnbindImportTasks',
|
||||||
|
permissions: [AUGUST_PERMISSIONS.phoneAssetAssociation.unbindImportPage],
|
||||||
|
keepAlive: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 解绑导入任务详情
|
||||||
|
{
|
||||||
|
path: 'phone-asset-unbind-import-tasks/detail',
|
||||||
|
name: 'PhoneAssetUnbindImportTaskDetail',
|
||||||
|
component: RoutesAlias.PhoneAssetUnbindImportTaskDetail,
|
||||||
|
meta: {
|
||||||
|
title: 'menus.assetManagement.phoneAssetUnbindImportTaskDetail',
|
||||||
|
permissions: [AUGUST_PERMISSIONS.phoneAssetAssociation.unbindImportDetail],
|
||||||
|
isHide: true,
|
||||||
|
keepAlive: false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -1164,6 +1164,19 @@ export const asyncRoutes: AppRouteRecord[] = [
|
|||||||
permissions: [AUGUST_PERMISSIONS.h5PopupConfiguration.list]
|
permissions: [AUGUST_PERMISSIONS.h5PopupConfiguration.list]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// H5运营弹窗配置详情
|
||||||
|
{
|
||||||
|
path: 'h5-popup-configuration/detail/:id',
|
||||||
|
name: 'H5PopupConfigurationDetail',
|
||||||
|
component: RoutesAlias.H5PopupConfigurationDetail,
|
||||||
|
meta: {
|
||||||
|
title: 'menus.settings.h5PopupConfigurationDetail',
|
||||||
|
isHide: true,
|
||||||
|
keepAlive: false,
|
||||||
|
allowedUserTypes: [1, 2],
|
||||||
|
permissions: [AUGUST_PERMISSIONS.h5PopupConfiguration.list]
|
||||||
|
}
|
||||||
|
},
|
||||||
// 资产钱包自动续费配置
|
// 资产钱包自动续费配置
|
||||||
{
|
{
|
||||||
path: 'asset-wallet-auto-renewal',
|
path: 'asset-wallet-auto-renewal',
|
||||||
|
|||||||
@@ -127,6 +127,7 @@ export enum RoutesAlias {
|
|||||||
WecomMembers = '/settings/wecom/members', // 企业微信成员
|
WecomMembers = '/settings/wecom/members', // 企业微信成员
|
||||||
WecomScenes = '/settings/wecom/scenes', // 企业微信审批场景
|
WecomScenes = '/settings/wecom/scenes', // 企业微信审批场景
|
||||||
H5PopupConfigurations = '/settings/h5-popup-configuration', // H5运营弹窗配置
|
H5PopupConfigurations = '/settings/h5-popup-configuration', // H5运营弹窗配置
|
||||||
|
H5PopupConfigurationDetail = '/settings/h5-popup-configuration/detail', // H5运营弹窗配置详情
|
||||||
AssetWalletAutoRenewal = '/settings/asset-wallet-auto-renewal', // 资产钱包自动续费配置
|
AssetWalletAutoRenewal = '/settings/asset-wallet-auto-renewal', // 资产钱包自动续费配置
|
||||||
|
|
||||||
// 轮询管理
|
// 轮询管理
|
||||||
@@ -153,8 +154,8 @@ export enum RoutesAlias {
|
|||||||
|
|
||||||
// 手机号资产关联
|
// 手机号资产关联
|
||||||
PhoneAssetAssociation = '/asset-management/phone-asset-association', // 手机号资产关联列表
|
PhoneAssetAssociation = '/asset-management/phone-asset-association', // 手机号资产关联列表
|
||||||
PhoneAssetUnbindImportTasks = '/asset-management/phone-asset-association/unbind-import-tasks', // 解绑导入任务列表
|
PhoneAssetUnbindImportTasks = '/asset-management/task-management/phone-asset-unbind-import-tasks', // 解绑导入任务列表
|
||||||
PhoneAssetUnbindImportTaskDetail = '/asset-management/phone-asset-association/unbind-import-tasks/detail', // 解绑导入任务详情
|
PhoneAssetUnbindImportTaskDetail = '/asset-management/task-management/phone-asset-unbind-import-tasks/detail', // 解绑导入任务详情
|
||||||
|
|
||||||
// 运营报表
|
// 运营报表
|
||||||
OperationsActivationReport = '/operations-reports/activation', // 设备激活报表
|
OperationsActivationReport = '/operations-reports/activation', // 设备激活报表
|
||||||
|
|||||||
@@ -171,8 +171,8 @@ export interface EmployeeCollectionBillQueryParams extends PaginationParams {
|
|||||||
status?: EmployeeCollectionBillStatus
|
status?: EmployeeCollectionBillStatus
|
||||||
debtor_account_id?: number
|
debtor_account_id?: number
|
||||||
customer_id?: number
|
customer_id?: number
|
||||||
created_from?: string
|
start_time?: string
|
||||||
created_to?: string
|
end_time?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CloseEmployeeCollectionBillRequest {
|
export interface CloseEmployeeCollectionBillRequest {
|
||||||
@@ -195,7 +195,7 @@ export interface EmployeeCollectionApplicationAttempt {
|
|||||||
approval_status_name?: string | null
|
approval_status_name?: string | null
|
||||||
approval_opinion?: string | null
|
approval_opinion?: string | null
|
||||||
acting_reason?: string | null
|
acting_reason?: string | null
|
||||||
external_transaction_no?: string | null
|
external_transaction_no?: string | null // 人工确认的外部交易流水号,申请人可原样核对与重用
|
||||||
paid_amount?: number | null
|
paid_amount?: number | null
|
||||||
paid_at?: string | null
|
paid_at?: string | null
|
||||||
payer_name?: string | null
|
payer_name?: string | null
|
||||||
@@ -220,7 +220,7 @@ export interface EmployeeCollectionApplication {
|
|||||||
paid_amount?: number | null // 人工确认的付款金额(分)
|
paid_amount?: number | null // 人工确认的付款金额(分)
|
||||||
paid_at?: string | null
|
paid_at?: string | null
|
||||||
payer_name?: string | null
|
payer_name?: string | null
|
||||||
external_transaction_no?: string | null
|
external_transaction_no?: string | null // 人工确认的外部交易流水号,申请人可原样核对与重用
|
||||||
payment_voucher_keys?: string[] | null
|
payment_voucher_keys?: string[] | null
|
||||||
remark?: string | null
|
remark?: string | null
|
||||||
status: EmployeeCollectionApplicationStatus
|
status: EmployeeCollectionApplicationStatus
|
||||||
|
|||||||
@@ -6,11 +6,14 @@
|
|||||||
// 命中页面集合 (home:首页, asset_detail:资产详情, package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值)
|
// 命中页面集合 (home:首页, asset_detail:资产详情, package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值)
|
||||||
export type H5PopupPage = 'home' | 'asset_detail' | 'package_purchase' | 'asset_wallet_recharge'
|
export type H5PopupPage = 'home' | 'asset_detail' | 'package_purchase' | 'asset_wallet_recharge'
|
||||||
|
|
||||||
|
// 弹窗类型(promotion: 套餐政策推广, announcement: 通用公告)
|
||||||
|
export type H5PopupType = 'promotion' | 'announcement'
|
||||||
|
|
||||||
// 投放频率 (once:每客户每配置版本仅一次, daily:每客户每配置版本每个上海自然日一次)
|
// 投放频率 (once:每客户每配置版本仅一次, daily:每客户每配置版本每个上海自然日一次)
|
||||||
export type H5PopupFrequency = 'once' | 'daily'
|
export type H5PopupFrequency = 'once' | 'daily'
|
||||||
|
|
||||||
// 受控动作 (package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值);空字符串表示无受控动作
|
// 受控动作 (package_purchase:套餐购买, asset_wallet_recharge:资产钱包充值);空字符串表示无受控动作
|
||||||
export type H5PopupActionType = 'package_purchase' | 'asset_wallet_recharge' | ''
|
export type H5PopupActionType = 'package_purchase' | 'asset_wallet_recharge'
|
||||||
|
|
||||||
// 卡类型范围 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信, CBN:中国广电)
|
// 卡类型范围 (CMCC:中国移动, CUCC:中国联通, CTCC:中国电信, CBN:中国广电)
|
||||||
export type H5PopupCardType = 'CMCC' | 'CUCC' | 'CTCC' | 'CBN'
|
export type H5PopupCardType = 'CMCC' | 'CUCC' | 'CTCC' | 'CBN'
|
||||||
@@ -20,6 +23,8 @@ export type H5PopupCardType = 'CMCC' | 'CUCC' | 'CTCC' | 'CBN'
|
|||||||
*/
|
*/
|
||||||
export interface H5PopupConfiguration {
|
export interface H5PopupConfiguration {
|
||||||
id: number // 配置ID
|
id: number // 配置ID
|
||||||
|
popup_type: H5PopupType // 弹窗类型
|
||||||
|
popup_type_text: string // 弹窗类型名称(中文)
|
||||||
title: string // 弹窗标题
|
title: string // 弹窗标题
|
||||||
content: string // 弹窗正文
|
content: string // 弹窗正文
|
||||||
pages: H5PopupPage[] | null // 命中页面集合;空数组等价于非法(页面必选)
|
pages: H5PopupPage[] | null // 命中页面集合;空数组等价于非法(页面必选)
|
||||||
@@ -28,7 +33,7 @@ export interface H5PopupConfiguration {
|
|||||||
frequency_text: string // 投放频率名称(中文)
|
frequency_text: string // 投放频率名称(中文)
|
||||||
enabled: boolean // 是否启用;停用后停止新投放,历史通知在展示期内仍可见
|
enabled: boolean // 是否启用;停用后停止新投放,历史通知在展示期内仍可见
|
||||||
enabled_text: string // 启停状态名称(中文)
|
enabled_text: string // 启停状态名称(中文)
|
||||||
action_type: H5PopupActionType // 受控动作;空值表示无受控动作
|
action_type: H5PopupActionType | null // 受控动作;空值表示无受控动作
|
||||||
shop_ids: number[] | null // 店铺范围;空数组表示全量
|
shop_ids: number[] | null // 店铺范围;空数组表示全量
|
||||||
device_types: string[] | null // 设备类型范围;空数组表示全量
|
device_types: string[] | null // 设备类型范围;空数组表示全量
|
||||||
card_types: H5PopupCardType[] | null // 卡类型范围;空数组表示全量
|
card_types: H5PopupCardType[] | null // 卡类型范围;空数组表示全量
|
||||||
@@ -68,6 +73,7 @@ export interface H5PopupConfigurationListResponse {
|
|||||||
* shop_ids/device_types/card_types 空数组 = 全量。
|
* shop_ids/device_types/card_types 空数组 = 全量。
|
||||||
*/
|
*/
|
||||||
export interface CreateH5PopupConfigurationRequest {
|
export interface CreateH5PopupConfigurationRequest {
|
||||||
|
popup_type: H5PopupType // 弹窗类型
|
||||||
title: string // 弹窗标题,1~100 字符
|
title: string // 弹窗标题,1~100 字符
|
||||||
content: string // 弹窗正文,1~2000 字符,不接受 HTML、URL 或前端路由
|
content: string // 弹窗正文,1~2000 字符,不接受 HTML、URL 或前端路由
|
||||||
pages: H5PopupPage[] // 命中页面集合(非空)
|
pages: H5PopupPage[] // 命中页面集合(非空)
|
||||||
@@ -76,7 +82,7 @@ export interface CreateH5PopupConfigurationRequest {
|
|||||||
starts_at: string // 生效开始时间(ISO 8601)
|
starts_at: string // 生效开始时间(ISO 8601)
|
||||||
ends_at: string // 生效结束时间(ISO 8601),不得早于开始时间
|
ends_at: string // 生效结束时间(ISO 8601),不得早于开始时间
|
||||||
enabled?: boolean // 是否启用,默认由后端决定
|
enabled?: boolean // 是否启用,默认由后端决定
|
||||||
action_type?: H5PopupActionType // 受控动作
|
action_type?: H5PopupActionType | null // 受控动作
|
||||||
shop_ids?: number[] // 店铺范围,空数组 = 全量
|
shop_ids?: number[] // 店铺范围,空数组 = 全量
|
||||||
device_types?: string[] // 设备类型范围,空数组 = 全量
|
device_types?: string[] // 设备类型范围,空数组 = 全量
|
||||||
card_types?: H5PopupCardType[] // 卡类型范围,空数组 = 全量
|
card_types?: H5PopupCardType[] // 卡类型范围,空数组 = 全量
|
||||||
@@ -90,6 +96,7 @@ export interface CreateH5PopupConfigurationRequest {
|
|||||||
*/
|
*/
|
||||||
export interface UpdateH5PopupConfigurationRequest {
|
export interface UpdateH5PopupConfigurationRequest {
|
||||||
id: number // 运营弹窗配置ID
|
id: number // 运营弹窗配置ID
|
||||||
|
popup_type?: H5PopupType // 弹窗类型
|
||||||
title?: string // 不传保持原值,1~100 字符
|
title?: string // 不传保持原值,1~100 字符
|
||||||
content?: string // 不传保持原值,1~2000 字符
|
content?: string // 不传保持原值,1~2000 字符
|
||||||
pages?: H5PopupPage[] // 不传保持原值;传空数组等价于非法(页面必选)
|
pages?: H5PopupPage[] // 不传保持原值;传空数组等价于非法(页面必选)
|
||||||
@@ -98,7 +105,7 @@ export interface UpdateH5PopupConfigurationRequest {
|
|||||||
starts_at?: string // 不传保持原值(ISO 8601)
|
starts_at?: string // 不传保持原值(ISO 8601)
|
||||||
ends_at?: string // 不传保持原值(ISO 8601),不得早于开始时间
|
ends_at?: string // 不传保持原值(ISO 8601),不得早于开始时间
|
||||||
enabled?: boolean // 不传保持原值;启停会刷新最近更新时间并影响同优先级排序
|
enabled?: boolean // 不传保持原值;启停会刷新最近更新时间并影响同优先级排序
|
||||||
action_type?: H5PopupActionType | null // 不传保持原值;传空字符串 = 清除受控动作
|
action_type?: H5PopupActionType | '' // 不传保持原值;传空字符串 = 清除受控动作
|
||||||
shop_ids?: number[] | null // 不传保持原值;传空数组 = 改为全量
|
shop_ids?: number[] | null // 不传保持原值;传空数组 = 改为全量
|
||||||
device_types?: string[] | null // 不传保持原值;传空数组 = 改为全量
|
device_types?: string[] | null // 不传保持原值;传空数组 = 改为全量
|
||||||
card_types?: H5PopupCardType[] | null // 不传保持原值;传空数组 = 改为全量
|
card_types?: H5PopupCardType[] | null // 不传保持原值;传空数组 = 改为全量
|
||||||
|
|||||||
@@ -6,6 +6,45 @@ import type { RequestOptions, ErrorMessageMode } from '@/types/api'
|
|||||||
import { normalizeApiError } from '@/utils/business/apiError'
|
import { normalizeApiError } from '@/utils/business/apiError'
|
||||||
import { syncCompatibleIdFields } from '@/utils/business/id'
|
import { syncCompatibleIdFields } from '@/utils/business/id'
|
||||||
|
|
||||||
|
const normalizeRfc3339Time = (value: unknown, isEndTime: boolean): unknown => {
|
||||||
|
if (value instanceof Date) {
|
||||||
|
return value.toISOString().replace(/\.\d{3}(?=Z$)/, '')
|
||||||
|
}
|
||||||
|
if (typeof value !== 'string' || !value) return value
|
||||||
|
|
||||||
|
const dateOnlyMatch = value.match(/^(\d{4}-\d{2}-\d{2})$/)
|
||||||
|
if (dateOnlyMatch) {
|
||||||
|
return `${dateOnlyMatch[1]}T${isEndTime ? '23:59:59' : '00:00:00'}+08:00`
|
||||||
|
}
|
||||||
|
|
||||||
|
const localDateTimeMatch = value.match(/^(\d{4}-\d{2}-\d{2})[ T](\d{2}:\d{2})(?::(\d{2}))?$/)
|
||||||
|
if (localDateTimeMatch) {
|
||||||
|
return `${localDateTimeMatch[1]}T${localDateTimeMatch[2]}:${localDateTimeMatch[3] || '00'}+08:00`
|
||||||
|
}
|
||||||
|
|
||||||
|
return value.replace(/\.\d{3}(?=Z$|[+-]\d{2}:\d{2}$)/, '')
|
||||||
|
}
|
||||||
|
|
||||||
|
const normalizeTimeQueryParams = (params: unknown) => {
|
||||||
|
if (
|
||||||
|
!params ||
|
||||||
|
typeof params !== 'object' ||
|
||||||
|
Array.isArray(params) ||
|
||||||
|
params instanceof URLSearchParams
|
||||||
|
) {
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryParams = params as Record<string, unknown>
|
||||||
|
if (!('start_time' in queryParams) && !('end_time' in queryParams)) return params
|
||||||
|
|
||||||
|
return {
|
||||||
|
...queryParams,
|
||||||
|
start_time: normalizeRfc3339Time(queryParams.start_time, false),
|
||||||
|
end_time: normalizeRfc3339Time(queryParams.end_time, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const axiosInstance = axios.create({
|
const axiosInstance = axios.create({
|
||||||
timeout: 15000, // 请求超时时间(毫秒)
|
timeout: 15000, // 请求超时时间(毫秒)
|
||||||
baseURL: import.meta.env.DEV ? '' : import.meta.env.VITE_API_URL, // 开发服务器使用代理,其他模式使用完整URL
|
baseURL: import.meta.env.DEV ? '' : import.meta.env.VITE_API_URL, // 开发服务器使用代理,其他模式使用完整URL
|
||||||
@@ -43,6 +82,7 @@ const axiosInstance = axios.create({
|
|||||||
// 请求拦截器
|
// 请求拦截器
|
||||||
axiosInstance.interceptors.request.use(
|
axiosInstance.interceptors.request.use(
|
||||||
(request: InternalAxiosRequestConfig) => {
|
(request: InternalAxiosRequestConfig) => {
|
||||||
|
request.params = normalizeTimeQueryParams(request.params)
|
||||||
const { accessToken } = useUserStore()
|
const { accessToken } = useUserStore()
|
||||||
|
|
||||||
// 如果 token 存在,则设置请求头
|
// 如果 token 存在,则设置请求头
|
||||||
|
|||||||
@@ -100,18 +100,6 @@
|
|||||||
当前暂无可用在线支付方式,暂时无法提交充值。
|
当前暂无可用在线支付方式,暂时无法提交充值。
|
||||||
</div>
|
</div>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem v-if="createMode === 'offline'" label="店铺" prop="shop_id">
|
|
||||||
<ElCascader
|
|
||||||
v-model="createForm.shop_id"
|
|
||||||
:options="shopCascadeOptions"
|
|
||||||
:props="shopCascadeProps"
|
|
||||||
placeholder="请选择店铺"
|
|
||||||
filterable
|
|
||||||
clearable
|
|
||||||
style="width: 100%"
|
|
||||||
@change="handleShopChange"
|
|
||||||
/>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem
|
<ElFormItem
|
||||||
v-if="createForm.payment_method === 'offline'"
|
v-if="createForm.payment_method === 'offline'"
|
||||||
label="支付凭证"
|
label="支付凭证"
|
||||||
@@ -127,26 +115,6 @@
|
|||||||
@change="handleVoucherChange"
|
@change="handleVoucherChange"
|
||||||
/>
|
/>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem
|
|
||||||
v-if="createMode === 'offline'"
|
|
||||||
label="收款方式"
|
|
||||||
prop="offline_payment_method_id"
|
|
||||||
>
|
|
||||||
<ElSelect
|
|
||||||
v-model="createForm.offline_payment_method_id"
|
|
||||||
placeholder="请选择收款方式"
|
|
||||||
style="width: 100%"
|
|
||||||
filterable
|
|
||||||
:loading="paymentMethodOptionsLoading"
|
|
||||||
>
|
|
||||||
<ElOption
|
|
||||||
v-for="item in offlinePaymentMethodOptions"
|
|
||||||
:key="item.id"
|
|
||||||
:label="item.name"
|
|
||||||
:value="item.id"
|
|
||||||
/>
|
|
||||||
</ElSelect>
|
|
||||||
</ElFormItem>
|
|
||||||
<ElFormItem
|
<ElFormItem
|
||||||
v-if="createForm.payment_method === 'offline'"
|
v-if="createForm.payment_method === 'offline'"
|
||||||
label="交易流水号"
|
label="交易流水号"
|
||||||
@@ -169,6 +137,47 @@
|
|||||||
<div class="external-transaction-tip">
|
<div class="external-transaction-tip">
|
||||||
识别结果仅供参考,请对照凭证核对后再提交。
|
识别结果仅供参考,请对照凭证核对后再提交。
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="ocrLoading" class="ocr-progress">
|
||||||
|
<ElProgress
|
||||||
|
:percentage="100"
|
||||||
|
:indeterminate="true"
|
||||||
|
:show-text="false"
|
||||||
|
:stroke-width="10"
|
||||||
|
/>
|
||||||
|
<div class="ocr-progress__text">正在识别支付凭证,请稍候...</div>
|
||||||
|
</div>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem v-if="createMode === 'offline'" label="店铺" prop="shop_id">
|
||||||
|
<ElCascader
|
||||||
|
v-model="createForm.shop_id"
|
||||||
|
:options="shopCascadeOptions"
|
||||||
|
:props="shopCascadeProps"
|
||||||
|
placeholder="请选择店铺"
|
||||||
|
filterable
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleShopChange"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem
|
||||||
|
v-if="createMode === 'offline'"
|
||||||
|
label="收款方式"
|
||||||
|
prop="offline_payment_method_id"
|
||||||
|
>
|
||||||
|
<ElSelect
|
||||||
|
v-model="createForm.offline_payment_method_id"
|
||||||
|
placeholder="请选择收款方式"
|
||||||
|
style="width: 100%"
|
||||||
|
filterable
|
||||||
|
:loading="paymentMethodOptionsLoading"
|
||||||
|
>
|
||||||
|
<ElOption
|
||||||
|
v-for="item in offlinePaymentMethodOptions"
|
||||||
|
:key="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</ElSelect>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
<ElFormItem v-if="createMode === 'offline'" label="其他凭证">
|
<ElFormItem v-if="createMode === 'offline'" label="其他凭证">
|
||||||
<VoucherUpload
|
<VoucherUpload
|
||||||
@@ -1624,6 +1633,17 @@
|
|||||||
color: var(--el-color-danger);
|
color: var(--el-color-danger);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ocr-progress {
|
||||||
|
margin-top: 10px;
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
margin-top: 6px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.external-transaction-row {
|
.external-transaction-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
|
|||||||
@@ -18,6 +18,15 @@
|
|||||||
@uploading-change="voucherUploading = $event"
|
@uploading-change="voucherUploading = $event"
|
||||||
@change="handleVoucherChange"
|
@change="handleVoucherChange"
|
||||||
/>
|
/>
|
||||||
|
<div v-if="ocrLoading" class="ocr-progress">
|
||||||
|
<div class="ocr-progress__text">正在识别付款凭证,请稍候...</div>
|
||||||
|
<ElProgress
|
||||||
|
:percentage="100"
|
||||||
|
:indeterminate="true"
|
||||||
|
:show-text="false"
|
||||||
|
:stroke-width="8"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</ElFormItem>
|
</ElFormItem>
|
||||||
|
|
||||||
<ElFormItem label="外部交易流水号" prop="external_transaction_no">
|
<ElFormItem label="外部交易流水号" prop="external_transaction_no">
|
||||||
@@ -67,81 +76,106 @@
|
|||||||
<ElFormItem label="核销账单" required>
|
<ElFormItem label="核销账单" required>
|
||||||
<div class="bill-selector">
|
<div class="bill-selector">
|
||||||
<div class="bill-selector__filters">
|
<div class="bill-selector__filters">
|
||||||
<div class="bill-selector__row">
|
<ElRow :gutter="8" class="bill-selector__row">
|
||||||
<ElSelect
|
<ElCol :span="6">
|
||||||
v-model="billSearch.source_type"
|
<ElSelect
|
||||||
placeholder="来源"
|
v-model="billSearch.source_type"
|
||||||
clearable
|
placeholder="来源"
|
||||||
class="bill-selector__filter"
|
clearable
|
||||||
>
|
class="bill-selector__filter"
|
||||||
<ElOption
|
>
|
||||||
v-for="opt in BILL_SOURCE_OPTIONS"
|
<ElOption
|
||||||
:key="opt.value"
|
v-for="opt in BILL_SOURCE_OPTIONS"
|
||||||
:label="opt.label"
|
:key="opt.value"
|
||||||
:value="opt.value"
|
:label="opt.label"
|
||||||
|
:value="opt.value"
|
||||||
|
/>
|
||||||
|
</ElSelect>
|
||||||
|
</ElCol>
|
||||||
|
<ElCol :span="6">
|
||||||
|
<ElInput
|
||||||
|
v-model="billSearch.source_no"
|
||||||
|
placeholder="来源单号"
|
||||||
|
clearable
|
||||||
|
class="bill-selector__filter"
|
||||||
/>
|
/>
|
||||||
</ElSelect>
|
</ElCol>
|
||||||
<ElInput
|
<ElCol :span="6">
|
||||||
v-model="billSearch.source_no"
|
<ElSelect
|
||||||
placeholder="来源单号"
|
v-model="billSearch.status"
|
||||||
clearable
|
placeholder="核销状态"
|
||||||
class="bill-selector__filter"
|
clearable
|
||||||
/>
|
class="bill-selector__filter"
|
||||||
<ElSelect
|
>
|
||||||
v-model="billSearch.status"
|
<ElOption
|
||||||
placeholder="核销状态"
|
v-for="opt in BILL_STATUS_OPTIONS"
|
||||||
clearable
|
:key="opt.value"
|
||||||
class="bill-selector__filter"
|
:label="opt.label"
|
||||||
>
|
:value="opt.value"
|
||||||
<ElOption
|
/>
|
||||||
v-for="opt in BILL_STATUS_OPTIONS"
|
</ElSelect>
|
||||||
:key="opt.value"
|
</ElCol>
|
||||||
:label="opt.label"
|
<ElCol :span="6">
|
||||||
:value="opt.value"
|
<ElSelect
|
||||||
|
v-model="billSearch.customer_id"
|
||||||
|
placeholder="店铺"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
reserve-keyword
|
||||||
|
:remote-method="searchBillShops"
|
||||||
|
class="bill-selector__filter"
|
||||||
|
>
|
||||||
|
<ElOption
|
||||||
|
v-for="shop in shopOptions"
|
||||||
|
:key="shop.id"
|
||||||
|
:label="shop.shop_name"
|
||||||
|
:value="shop.id"
|
||||||
|
/>
|
||||||
|
</ElSelect>
|
||||||
|
</ElCol>
|
||||||
|
</ElRow>
|
||||||
|
<ElRow :gutter="8" class="bill-selector__row">
|
||||||
|
<ElCol :span="6">
|
||||||
|
<ElDatePicker
|
||||||
|
v-model="billSearch.start_time"
|
||||||
|
type="date"
|
||||||
|
placeholder="开始日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
clearable
|
||||||
|
class="bill-selector__filter"
|
||||||
|
style="width: 100%; min-width: 0"
|
||||||
/>
|
/>
|
||||||
</ElSelect>
|
</ElCol>
|
||||||
<ElSelect
|
<ElCol :span="6">
|
||||||
v-model="billSearch.customer_id"
|
<ElDatePicker
|
||||||
placeholder="店铺"
|
v-model="billSearch.end_time"
|
||||||
clearable
|
type="date"
|
||||||
filterable
|
placeholder="结束日期"
|
||||||
remote
|
value-format="YYYY-MM-DD"
|
||||||
reserve-keyword
|
clearable
|
||||||
:remote-method="searchBillShops"
|
class="bill-selector__filter"
|
||||||
class="bill-selector__filter"
|
style="width: 100%; min-width: 0"
|
||||||
>
|
|
||||||
<ElOption
|
|
||||||
v-for="shop in shopOptions"
|
|
||||||
:key="shop.id"
|
|
||||||
:label="shop.shop_name"
|
|
||||||
:value="shop.id"
|
|
||||||
/>
|
/>
|
||||||
</ElSelect>
|
</ElCol>
|
||||||
</div>
|
<ElCol :span="6">
|
||||||
<div class="bill-selector__row">
|
<ElButton
|
||||||
<ElDatePicker
|
class="bill-selector__filter bill-selector__action"
|
||||||
v-model="billSearch.dateRange"
|
@click="handleBillReset"
|
||||||
type="daterange"
|
>
|
||||||
range-separator="至"
|
重置
|
||||||
start-placeholder="开始日期"
|
</ElButton>
|
||||||
end-placeholder="结束日期"
|
</ElCol>
|
||||||
value-format="YYYY-MM-DD"
|
<ElCol :span="6">
|
||||||
class="bill-selector__filter bill-selector__filter--span2"
|
<ElButton
|
||||||
/>
|
type="primary"
|
||||||
<ElButton
|
class="bill-selector__filter bill-selector__action"
|
||||||
class="bill-selector__filter bill-selector__action"
|
@click="handleBillSearch"
|
||||||
@click="handleBillReset"
|
>
|
||||||
>
|
搜索
|
||||||
重置
|
</ElButton>
|
||||||
</ElButton>
|
</ElCol>
|
||||||
<ElButton
|
</ElRow>
|
||||||
type="primary"
|
|
||||||
class="bill-selector__filter bill-selector__action"
|
|
||||||
@click="handleBillSearch"
|
|
||||||
>
|
|
||||||
搜索
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="billsLoading" class="bill-selector__empty">加载中...</div>
|
<div v-if="billsLoading" class="bill-selector__empty">加载中...</div>
|
||||||
@@ -340,9 +374,8 @@
|
|||||||
source_no: '',
|
source_no: '',
|
||||||
status: undefined as number | undefined,
|
status: undefined as number | undefined,
|
||||||
customer_id: undefined as number | undefined,
|
customer_id: undefined as number | undefined,
|
||||||
dateRange: [] as string[],
|
start_time: '',
|
||||||
created_from: '',
|
end_time: ''
|
||||||
created_to: ''
|
|
||||||
})
|
})
|
||||||
|
|
||||||
const billPagination = reactive({
|
const billPagination = reactive({
|
||||||
@@ -520,8 +553,8 @@
|
|||||||
source_no: billSearch.source_no.trim() || undefined,
|
source_no: billSearch.source_no.trim() || undefined,
|
||||||
status: billSearch.status,
|
status: billSearch.status,
|
||||||
customer_id: billSearch.customer_id,
|
customer_id: billSearch.customer_id,
|
||||||
created_from: billSearch.created_from || undefined,
|
start_time: billSearch.start_time || undefined,
|
||||||
created_to: billSearch.created_to || undefined
|
end_time: billSearch.end_time || undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
const loadCandidateBills = async () => {
|
const loadCandidateBills = async () => {
|
||||||
@@ -565,18 +598,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const syncBillCreatedRange = () => {
|
|
||||||
if (Array.isArray(billSearch.dateRange) && billSearch.dateRange.length === 2) {
|
|
||||||
billSearch.created_from = billSearch.dateRange[0]
|
|
||||||
billSearch.created_to = billSearch.dateRange[1]
|
|
||||||
} else {
|
|
||||||
billSearch.created_from = ''
|
|
||||||
billSearch.created_to = ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleBillSearch = () => {
|
const handleBillSearch = () => {
|
||||||
syncBillCreatedRange()
|
|
||||||
billPagination.page = 1
|
billPagination.page = 1
|
||||||
void loadCandidateBills()
|
void loadCandidateBills()
|
||||||
}
|
}
|
||||||
@@ -586,9 +608,8 @@
|
|||||||
billSearch.source_no = ''
|
billSearch.source_no = ''
|
||||||
billSearch.status = undefined
|
billSearch.status = undefined
|
||||||
billSearch.customer_id = undefined
|
billSearch.customer_id = undefined
|
||||||
billSearch.dateRange = []
|
billSearch.start_time = ''
|
||||||
billSearch.created_from = ''
|
billSearch.end_time = ''
|
||||||
billSearch.created_to = ''
|
|
||||||
billPagination.page = 1
|
billPagination.page = 1
|
||||||
void loadCandidateBills()
|
void loadCandidateBills()
|
||||||
}
|
}
|
||||||
@@ -650,9 +671,8 @@
|
|||||||
billSearch.source_no = ''
|
billSearch.source_no = ''
|
||||||
billSearch.status = undefined
|
billSearch.status = undefined
|
||||||
billSearch.customer_id = undefined
|
billSearch.customer_id = undefined
|
||||||
billSearch.dateRange = []
|
billSearch.start_time = ''
|
||||||
billSearch.created_from = ''
|
billSearch.end_time = ''
|
||||||
billSearch.created_to = ''
|
|
||||||
billPagination.page = 1
|
billPagination.page = 1
|
||||||
billPagination.total = 0
|
billPagination.total = 0
|
||||||
candidateBills.value = []
|
candidateBills.value = []
|
||||||
@@ -863,10 +883,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__row {
|
&__row {
|
||||||
display: grid;
|
width: 100%;
|
||||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
||||||
gap: 8px;
|
|
||||||
align-items: center;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__filter {
|
&__filter {
|
||||||
@@ -874,10 +891,6 @@
|
|||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__filter--span2 {
|
|
||||||
grid-column: span 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__action {
|
&__action {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
@@ -953,4 +966,20 @@
|
|||||||
line-height: 18px;
|
line-height: 18px;
|
||||||
color: var(--el-text-color-secondary);
|
color: var(--el-text-color-secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.ocr-progress {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
border: 1px solid var(--el-color-primary-light-7);
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
&__text {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -56,15 +56,14 @@
|
|||||||
<template #header>
|
<template #header>
|
||||||
<div class="block-title">付款凭证</div>
|
<div class="block-title">付款凭证</div>
|
||||||
</template>
|
</template>
|
||||||
<ElButton
|
<PaymentVoucherDialog
|
||||||
v-if="
|
v-if="
|
||||||
voucherKeys.length &&
|
voucherKeys.length &&
|
||||||
hasAuth(AUGUST_PERMISSIONS.employeeCollection.applicationViewVoucher)
|
hasAuth(AUGUST_PERMISSIONS.employeeCollection.applicationViewVoucher)
|
||||||
"
|
"
|
||||||
@click="voucherVisible = true"
|
inline
|
||||||
>
|
:file-keys="voucherKeys"
|
||||||
查看付款凭证
|
/>
|
||||||
</ElButton>
|
|
||||||
<span v-else class="empty-text">暂无付款凭证</span>
|
<span v-else class="empty-text">暂无付款凭证</span>
|
||||||
</ElCard>
|
</ElCard>
|
||||||
|
|
||||||
@@ -102,8 +101,6 @@
|
|||||||
</ElTimelineItem>
|
</ElTimelineItem>
|
||||||
</ElTimeline>
|
</ElTimeline>
|
||||||
</ElCard>
|
</ElCard>
|
||||||
|
|
||||||
<PaymentVoucherDialog :file-keys="voucherKeys" @close="voucherVisible = false" />
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -150,7 +147,6 @@
|
|||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const detail = ref<EmployeeCollectionApplicationDetailData | null>(null)
|
const detail = ref<EmployeeCollectionApplicationDetailData | null>(null)
|
||||||
const voucherVisible = ref(false)
|
|
||||||
|
|
||||||
const application = computed<EmployeeCollectionApplication | null>(
|
const application = computed<EmployeeCollectionApplication | null>(
|
||||||
() => detail.value?.application || null
|
() => detail.value?.application || null
|
||||||
@@ -169,7 +165,11 @@
|
|||||||
{
|
{
|
||||||
title: '申请信息',
|
title: '申请信息',
|
||||||
fields: [
|
fields: [
|
||||||
{ label: '申请编号', prop: 'id', formatter: (value) => (value ? `#${value}` : '-') },
|
{
|
||||||
|
label: '外部交易流水号',
|
||||||
|
prop: 'external_transaction_no',
|
||||||
|
formatter: (value) => value || '-'
|
||||||
|
},
|
||||||
{ label: '收款方式', prop: 'payment_method_name', formatter: (value) => value || '-' },
|
{ label: '收款方式', prop: 'payment_method_name', formatter: (value) => value || '-' },
|
||||||
{
|
{
|
||||||
label: '付款金额',
|
label: '付款金额',
|
||||||
@@ -181,11 +181,6 @@
|
|||||||
prop: 'paid_at',
|
prop: 'paid_at',
|
||||||
formatter: (value) => formatDateTime(value)
|
formatter: (value) => formatDateTime(value)
|
||||||
},
|
},
|
||||||
{
|
|
||||||
label: '外部交易流水号',
|
|
||||||
prop: 'external_transaction_no',
|
|
||||||
formatter: (value) => value || '-'
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: '状态',
|
label: '状态',
|
||||||
prop: 'status',
|
prop: 'status',
|
||||||
|
|||||||
@@ -136,7 +136,7 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
const columnOptions = [
|
const columnOptions = [
|
||||||
{ label: '申请编号', prop: 'id' },
|
{ label: '外部交易流水号', prop: 'external_transaction_no' },
|
||||||
{ label: '收款方式', prop: 'payment_method_name' },
|
{ label: '收款方式', prop: 'payment_method_name' },
|
||||||
{ label: '付款金额', prop: 'paid_amount' },
|
{ label: '付款金额', prop: 'paid_amount' },
|
||||||
{ label: '状态', prop: 'status' },
|
{ label: '状态', prop: 'status' },
|
||||||
@@ -149,9 +149,9 @@
|
|||||||
|
|
||||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||||
{
|
{
|
||||||
prop: 'id',
|
prop: 'external_transaction_no',
|
||||||
label: '申请编号',
|
label: '外部交易流水号',
|
||||||
minWidth: 140,
|
minWidth: 220,
|
||||||
formatter: (row: EmployeeCollectionApplication) =>
|
formatter: (row: EmployeeCollectionApplication) =>
|
||||||
h(
|
h(
|
||||||
'span',
|
'span',
|
||||||
@@ -159,7 +159,7 @@
|
|||||||
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||||
onClick: () => handleViewDetail(row)
|
onClick: () => handleViewDetail(row)
|
||||||
},
|
},
|
||||||
`#${row.id}`
|
row.external_transaction_no || '-'
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -167,8 +167,8 @@
|
|||||||
status: undefined as number | undefined,
|
status: undefined as number | undefined,
|
||||||
customer_id: undefined as number | undefined,
|
customer_id: undefined as number | undefined,
|
||||||
dateRange: [] as string[],
|
dateRange: [] as string[],
|
||||||
created_from: '',
|
start_time: '',
|
||||||
created_to: ''
|
end_time: ''
|
||||||
}
|
}
|
||||||
const searchForm = reactive({ ...initialSearchState })
|
const searchForm = reactive({ ...initialSearchState })
|
||||||
const pagination = reactive({ page: 1, page_size: 20, total: 0 })
|
const pagination = reactive({ page: 1, page_size: 20, total: 0 })
|
||||||
@@ -258,8 +258,8 @@
|
|||||||
source_no: searchForm.source_no.trim() || undefined,
|
source_no: searchForm.source_no.trim() || undefined,
|
||||||
status: searchForm.status,
|
status: searchForm.status,
|
||||||
customer_id: searchForm.customer_id,
|
customer_id: searchForm.customer_id,
|
||||||
created_from: searchForm.created_from || undefined,
|
start_time: searchForm.start_time || undefined,
|
||||||
created_to: searchForm.created_to || undefined
|
end_time: searchForm.end_time || undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
const searchShops = async (query: string) => {
|
const searchShops = async (query: string) => {
|
||||||
@@ -391,11 +391,11 @@
|
|||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
if (Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
|
if (Array.isArray(searchForm.dateRange) && searchForm.dateRange.length === 2) {
|
||||||
searchForm.created_from = searchForm.dateRange[0]
|
searchForm.start_time = searchForm.dateRange[0]
|
||||||
searchForm.created_to = searchForm.dateRange[1]
|
searchForm.end_time = searchForm.dateRange[1]
|
||||||
} else {
|
} else {
|
||||||
searchForm.created_from = ''
|
searchForm.start_time = ''
|
||||||
searchForm.created_to = ''
|
searchForm.end_time = ''
|
||||||
}
|
}
|
||||||
pagination.page = 1
|
pagination.page = 1
|
||||||
reload()
|
reload()
|
||||||
|
|||||||
226
src/views/settings/h5-popup-configuration/detail.vue
Normal file
226
src/views/settings/h5-popup-configuration/detail.vue
Normal file
@@ -0,0 +1,226 @@
|
|||||||
|
<template>
|
||||||
|
<div class="h5-popup-configuration-detail-page">
|
||||||
|
<ElCard shadow="never">
|
||||||
|
<div class="detail-header">
|
||||||
|
<ElButton @click="handleBack">
|
||||||
|
<template #icon>
|
||||||
|
<ElIcon><ArrowLeft /></ElIcon>
|
||||||
|
</template>
|
||||||
|
返回
|
||||||
|
</ElButton>
|
||||||
|
<h2 class="detail-title">H5运营弹窗配置详情</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DetailPage v-if="configuration" :sections="detailSections" :data="configuration" />
|
||||||
|
|
||||||
|
<div v-if="loading" class="loading-container">
|
||||||
|
<ElIcon class="is-loading"><Loading /></ElIcon>
|
||||||
|
<span>加载中...</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ElEmpty v-else-if="!configuration" description="未找到运营弹窗配置" />
|
||||||
|
</ElCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { computed, h, onMounted, ref } from 'vue'
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { ElButton, ElCard, ElIcon, ElMessage, ElTag } from 'element-plus'
|
||||||
|
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
|
||||||
|
import DetailPage from '@/components/common/DetailPage.vue'
|
||||||
|
import type { DetailSection } from '@/components/common/DetailPage.vue'
|
||||||
|
import { H5PopupConfigurationService } from '@/api/modules'
|
||||||
|
import type { H5PopupConfiguration, H5PopupPage, H5PopupType } from '@/types/api'
|
||||||
|
import { RoutesAlias } from '@/router/routesAlias'
|
||||||
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
|
import { getErrorMessage } from '@/utils/business'
|
||||||
|
|
||||||
|
defineOptions({ name: 'H5PopupConfigurationDetail' })
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
const loading = ref(false)
|
||||||
|
const configuration = ref<H5PopupConfiguration | null>(null)
|
||||||
|
|
||||||
|
const popupTypeNames: Record<H5PopupType, string> = {
|
||||||
|
promotion: '套餐政策推广',
|
||||||
|
announcement: '通用公告'
|
||||||
|
}
|
||||||
|
const pageNames: Record<H5PopupPage, string> = {
|
||||||
|
home: '首页',
|
||||||
|
asset_detail: '资产详情',
|
||||||
|
package_purchase: '套餐购买',
|
||||||
|
asset_wallet_recharge: '资产钱包充值'
|
||||||
|
}
|
||||||
|
const frequencyNames = {
|
||||||
|
once: '每客户每配置版本仅一次',
|
||||||
|
daily: '每客户每配置版本每个上海自然日一次'
|
||||||
|
}
|
||||||
|
const actionTypeNames: Record<string, string> = {
|
||||||
|
package_purchase: '套餐购买',
|
||||||
|
asset_wallet_recharge: '资产钱包充值'
|
||||||
|
}
|
||||||
|
const cardTypeNames: Record<string, string> = {
|
||||||
|
CMCC: '中国移动',
|
||||||
|
CUCC: '中国联通',
|
||||||
|
CTCC: '中国电信',
|
||||||
|
CBN: '中国广电'
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatList = (
|
||||||
|
values: Array<string | number> | null | undefined,
|
||||||
|
formatter?: (value: string) => string
|
||||||
|
) => {
|
||||||
|
if (!values?.length) return '全部'
|
||||||
|
return values.map((value) => formatter?.(String(value)) || String(value)).join('、')
|
||||||
|
}
|
||||||
|
|
||||||
|
const detailSections = computed((): DetailSection[] => [
|
||||||
|
{
|
||||||
|
title: '基础信息',
|
||||||
|
fields: [
|
||||||
|
{ label: '配置ID', prop: 'id' },
|
||||||
|
{ label: '版本', prop: 'version' },
|
||||||
|
{
|
||||||
|
label: '弹窗类型',
|
||||||
|
render: (data: H5PopupConfiguration) =>
|
||||||
|
h(
|
||||||
|
ElTag,
|
||||||
|
{ type: 'primary' },
|
||||||
|
() => data.popup_type_text || popupTypeNames[data.popup_type]
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '启停状态',
|
||||||
|
render: (data: H5PopupConfiguration) =>
|
||||||
|
h(
|
||||||
|
ElTag,
|
||||||
|
{ type: data.enabled ? 'success' : 'info' },
|
||||||
|
() => data.enabled_text || (data.enabled ? '启用' : '停用')
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ label: '标题', prop: 'title', fullWidth: true },
|
||||||
|
{ label: '正文', prop: 'content', fullWidth: true }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '投放配置',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: '命中页面',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) =>
|
||||||
|
formatList(data.pages, (value) => pageNames[value as H5PopupPage] || value),
|
||||||
|
fullWidth: true
|
||||||
|
},
|
||||||
|
{ label: '优先级', prop: 'priority' },
|
||||||
|
{
|
||||||
|
label: '投放频率',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) =>
|
||||||
|
data.frequency_text || frequencyNames[data.frequency] || data.frequency
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '受控动作',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) =>
|
||||||
|
actionTypeNames[data.action_type || ''] || '无'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '生效开始',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) => formatDateTime(data.starts_at)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '生效结束',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) => formatDateTime(data.ends_at)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '投放范围与审计信息',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
label: '店铺范围',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) => formatList(data.shop_ids),
|
||||||
|
fullWidth: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '设备类型范围',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) => formatList(data.device_types),
|
||||||
|
fullWidth: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '卡类型范围',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) =>
|
||||||
|
formatList(data.card_types, (value) => cardTypeNames[value] || value),
|
||||||
|
fullWidth: true
|
||||||
|
},
|
||||||
|
{ label: '创建人', prop: 'creator' },
|
||||||
|
{
|
||||||
|
label: '创建时间',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) =>
|
||||||
|
formatDateTime(data.created_at)
|
||||||
|
},
|
||||||
|
{ label: '最近更新人', prop: 'updater' },
|
||||||
|
{
|
||||||
|
label: '最近更新时间',
|
||||||
|
formatter: (_value: unknown, data: H5PopupConfiguration) =>
|
||||||
|
formatDateTime(data.updated_at)
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const loadDetail = async () => {
|
||||||
|
const id = Number(route.params.id)
|
||||||
|
if (!id) {
|
||||||
|
ElMessage.error('无效的配置ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const res = await H5PopupConfigurationService.getConfiguration(id)
|
||||||
|
if (res.code === 0) configuration.value = res.data
|
||||||
|
else ElMessage.error(res.msg || '获取运营弹窗配置详情失败')
|
||||||
|
} catch (error) {
|
||||||
|
ElMessage.error(getErrorMessage(error, '获取运营弹窗配置详情失败'))
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBack = () => {
|
||||||
|
router.push(RoutesAlias.H5PopupConfigurations)
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => void loadDetail())
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.h5-popup-configuration-detail-page {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-header {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-bottom: 16px;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-lighter);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-title {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 240px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -52,6 +52,16 @@
|
|||||||
@closed="resetForm"
|
@closed="resetForm"
|
||||||
>
|
>
|
||||||
<ElForm ref="formRef" :model="form" :rules="formRules" label-width="110px">
|
<ElForm ref="formRef" :model="form" :rules="formRules" label-width="110px">
|
||||||
|
<ElFormItem label="弹窗类型" prop="popup_type">
|
||||||
|
<ElSelect v-model="form.popup_type" placeholder="请选择弹窗类型" style="width: 100%">
|
||||||
|
<ElOption
|
||||||
|
v-for="item in POPUP_TYPE_OPTIONS"
|
||||||
|
:key="item.value"
|
||||||
|
:label="item.label"
|
||||||
|
:value="item.value"
|
||||||
|
/>
|
||||||
|
</ElSelect>
|
||||||
|
</ElFormItem>
|
||||||
<ElFormItem label="弹窗标题" prop="title">
|
<ElFormItem label="弹窗标题" prop="title">
|
||||||
<ElInput
|
<ElInput
|
||||||
v-model="form.title"
|
v-model="form.title"
|
||||||
@@ -174,60 +184,12 @@
|
|||||||
</ElButton>
|
</ElButton>
|
||||||
</template>
|
</template>
|
||||||
</ElDialog>
|
</ElDialog>
|
||||||
|
|
||||||
<!-- 详情 -->
|
|
||||||
<ElDialog v-model="detailDialogVisible" title="运营弹窗配置详情" width="680px">
|
|
||||||
<div v-if="detailLoading" class="detail-loading">加载中...</div>
|
|
||||||
<ElDescriptions v-else-if="detail" :column="2" border>
|
|
||||||
<ElDescriptionsItem label="配置ID">{{ detail.id }}</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="版本">{{ detail.version }}</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="标题" :span="2">{{ detail.title }}</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="正文" :span="2">
|
|
||||||
<span class="detail-content">{{ detail.content }}</span>
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="命中页面" :span="2">
|
|
||||||
{{ formatPages(detail.pages) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="店铺范围">
|
|
||||||
{{ formatScope(detail.shop_ids) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="设备类型范围">
|
|
||||||
{{ formatScope(detail.device_types) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="卡类型范围">
|
|
||||||
{{ formatCardTypes(detail.card_types) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="优先级">{{ detail.priority }}</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="投放频率">
|
|
||||||
{{ detail.frequency_text || FREQUENCY_NAMES[detail.frequency] || detail.frequency }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="启停状态">
|
|
||||||
{{ detail.enabled_text || (detail.enabled ? '启用' : '停用') }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="受控动作">
|
|
||||||
{{ ACTION_TYPE_NAMES[detail.action_type] || '无' }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="生效开始" :span="2">
|
|
||||||
{{ formatDateTime(detail.starts_at) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="生效结束" :span="2">
|
|
||||||
{{ formatDateTime(detail.ends_at) }}
|
|
||||||
</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="创建人">{{ detail.creator }}</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="创建时间">{{
|
|
||||||
formatDateTime(detail.created_at)
|
|
||||||
}}</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="最近更新人">{{ detail.updater }}</ElDescriptionsItem>
|
|
||||||
<ElDescriptionsItem label="最近更新时间">{{
|
|
||||||
formatDateTime(detail.updated_at)
|
|
||||||
}}</ElDescriptionsItem>
|
|
||||||
</ElDescriptions>
|
|
||||||
</ElDialog>
|
|
||||||
</ArtTableFullScreen>
|
</ArtTableFullScreen>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, h, onMounted, reactive, ref } from 'vue'
|
import { computed, h, onMounted, reactive, ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
import { ElMessage, ElMessageBox, ElTag, FormInstance, FormRules } from 'element-plus'
|
import { ElMessage, ElMessageBox, ElTag, FormInstance, FormRules } from 'element-plus'
|
||||||
import { Plus } from '@element-plus/icons-vue'
|
import { Plus } from '@element-plus/icons-vue'
|
||||||
import { H5PopupConfigurationService, ShopService } from '@/api/modules'
|
import { H5PopupConfigurationService, ShopService } from '@/api/modules'
|
||||||
@@ -242,12 +204,14 @@
|
|||||||
H5PopupCardType,
|
H5PopupCardType,
|
||||||
H5PopupConfiguration,
|
H5PopupConfiguration,
|
||||||
H5PopupFrequency,
|
H5PopupFrequency,
|
||||||
H5PopupPage
|
H5PopupPage,
|
||||||
|
H5PopupType
|
||||||
} from '@/types/api'
|
} from '@/types/api'
|
||||||
|
|
||||||
defineOptions({ name: 'H5PopupConfiguration' })
|
defineOptions({ name: 'H5PopupConfiguration' })
|
||||||
|
|
||||||
const { hasAuth } = useAuth()
|
const { hasAuth } = useAuth()
|
||||||
|
const router = useRouter()
|
||||||
const perms = AUGUST_PERMISSIONS.h5PopupConfiguration
|
const perms = AUGUST_PERMISSIONS.h5PopupConfiguration
|
||||||
|
|
||||||
const canCreate = computed(() => hasAuth(perms.create))
|
const canCreate = computed(() => hasAuth(perms.create))
|
||||||
@@ -267,6 +231,14 @@
|
|||||||
{ label: '套餐购买', value: 'package_purchase' },
|
{ label: '套餐购买', value: 'package_purchase' },
|
||||||
{ label: '资产钱包充值', value: 'asset_wallet_recharge' }
|
{ label: '资产钱包充值', value: 'asset_wallet_recharge' }
|
||||||
]
|
]
|
||||||
|
const POPUP_TYPE_NAMES: Record<H5PopupType, string> = {
|
||||||
|
promotion: '套餐政策推广',
|
||||||
|
announcement: '通用公告'
|
||||||
|
}
|
||||||
|
const POPUP_TYPE_OPTIONS: Array<{ label: string; value: H5PopupType }> = [
|
||||||
|
{ label: '套餐政策推广', value: 'promotion' },
|
||||||
|
{ label: '通用公告', value: 'announcement' }
|
||||||
|
]
|
||||||
const FREQUENCY_NAMES: Record<H5PopupFrequency, string> = {
|
const FREQUENCY_NAMES: Record<H5PopupFrequency, string> = {
|
||||||
once: '每客户每配置版本仅一次',
|
once: '每客户每配置版本仅一次',
|
||||||
daily: '每客户每配置版本每个上海自然日一次'
|
daily: '每客户每配置版本每个上海自然日一次'
|
||||||
@@ -322,6 +294,7 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
const columnOptions = [
|
const columnOptions = [
|
||||||
|
{ label: '弹窗类型', prop: 'popup_type' },
|
||||||
{ label: '标题', prop: 'title' },
|
{ label: '标题', prop: 'title' },
|
||||||
{ label: '命中页面', prop: 'pages' },
|
{ label: '命中页面', prop: 'pages' },
|
||||||
{ label: '范围', prop: 'scope' },
|
{ label: '范围', prop: 'scope' },
|
||||||
@@ -341,15 +314,6 @@
|
|||||||
return pages.map((page) => PAGE_NAMES[page] || page).join('、')
|
return pages.map((page) => PAGE_NAMES[page] || page).join('、')
|
||||||
}
|
}
|
||||||
|
|
||||||
const formatScope = (values: string[] | number[] | null | undefined): string => {
|
|
||||||
if (!values || values.length === 0) return '全部'
|
|
||||||
const text = values
|
|
||||||
.slice(0, 3)
|
|
||||||
.map((item) => (typeof item === 'number' && item !== 0 ? `#${item}` : String(item)))
|
|
||||||
.join('、')
|
|
||||||
return values.length > 3 ? `${text} 等${values.length}项` : text
|
|
||||||
}
|
|
||||||
|
|
||||||
const formatCardTypes = (values: H5PopupCardType[] | null | undefined): string => {
|
const formatCardTypes = (values: H5PopupCardType[] | null | undefined): string => {
|
||||||
if (!values || values.length === 0) return '全部'
|
if (!values || values.length === 0) return '全部'
|
||||||
return values.map((item) => CARD_TYPE_NAMES[item] || item).join('、')
|
return values.map((item) => CARD_TYPE_NAMES[item] || item).join('、')
|
||||||
@@ -370,7 +334,31 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||||
{ prop: 'title', label: '标题', minWidth: 180, showOverflowTooltip: true },
|
{
|
||||||
|
prop: 'popup_type',
|
||||||
|
label: '弹窗类型',
|
||||||
|
width: 120,
|
||||||
|
formatter: (row: H5PopupConfiguration) =>
|
||||||
|
row.popup_type_text || POPUP_TYPE_NAMES[row.popup_type] || row.popup_type
|
||||||
|
},
|
||||||
|
{
|
||||||
|
prop: 'title',
|
||||||
|
label: '标题',
|
||||||
|
minWidth: 180,
|
||||||
|
showOverflowTooltip: true,
|
||||||
|
formatter: (row: H5PopupConfiguration) =>
|
||||||
|
h(
|
||||||
|
'span',
|
||||||
|
{
|
||||||
|
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||||
|
onClick: (event: MouseEvent) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
goDetail(row)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
row.title
|
||||||
|
)
|
||||||
|
},
|
||||||
{
|
{
|
||||||
prop: 'pages',
|
prop: 'pages',
|
||||||
label: '命中页面',
|
label: '命中页面',
|
||||||
@@ -409,7 +397,7 @@
|
|||||||
prop: 'action_type',
|
prop: 'action_type',
|
||||||
label: '受控动作',
|
label: '受控动作',
|
||||||
width: 120,
|
width: 120,
|
||||||
formatter: (row: H5PopupConfiguration) => ACTION_TYPE_NAMES[row.action_type] || '无'
|
formatter: (row: H5PopupConfiguration) => ACTION_TYPE_NAMES[row.action_type || ''] || '无'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'effective_time',
|
prop: 'effective_time',
|
||||||
@@ -500,10 +488,15 @@
|
|||||||
|
|
||||||
// ========== 行操作 ==========
|
// ========== 行操作 ==========
|
||||||
|
|
||||||
|
const goDetail = (row: H5PopupConfiguration) => {
|
||||||
|
router.push({
|
||||||
|
name: 'H5PopupConfigurationDetail',
|
||||||
|
params: { id: String(row.id) }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
const getActions = (row: H5PopupConfiguration) => {
|
const getActions = (row: H5PopupConfiguration) => {
|
||||||
const actions: any[] = [
|
const actions: any[] = []
|
||||||
{ label: '详情', handler: () => openDetailDialog(row), type: 'primary' }
|
|
||||||
]
|
|
||||||
if (canUpdate.value) {
|
if (canUpdate.value) {
|
||||||
actions.push({ label: '编辑', handler: () => openEditDialog(row) })
|
actions.push({ label: '编辑', handler: () => openEditDialog(row) })
|
||||||
}
|
}
|
||||||
@@ -555,33 +548,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========== 详情 ==========
|
|
||||||
|
|
||||||
const detailDialogVisible = ref(false)
|
|
||||||
const detailLoading = ref(false)
|
|
||||||
const detail = ref<H5PopupConfiguration | null>(null)
|
|
||||||
|
|
||||||
const openDetailDialog = async (row: H5PopupConfiguration) => {
|
|
||||||
detail.value = null
|
|
||||||
detailDialogVisible.value = true
|
|
||||||
detailLoading.value = true
|
|
||||||
try {
|
|
||||||
const res = await H5PopupConfigurationService.getConfiguration(row.id)
|
|
||||||
if (res.code === 0 && res.data) {
|
|
||||||
detail.value = res.data
|
|
||||||
} else {
|
|
||||||
ElMessage.error(res.msg || '获取配置详情失败')
|
|
||||||
detailDialogVisible.value = false
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('获取运营弹窗配置详情失败:', error)
|
|
||||||
ElMessage.error(normalizeApiError(error).message || '获取配置详情失败')
|
|
||||||
detailDialogVisible.value = false
|
|
||||||
} finally {
|
|
||||||
detailLoading.value = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========== 创建 / 编辑 ==========
|
// ========== 创建 / 编辑 ==========
|
||||||
|
|
||||||
const formDialogVisible = ref(false)
|
const formDialogVisible = ref(false)
|
||||||
@@ -593,19 +559,21 @@
|
|||||||
|
|
||||||
const emptyForm = (): {
|
const emptyForm = (): {
|
||||||
id: number
|
id: number
|
||||||
|
popup_type: H5PopupType
|
||||||
title: string
|
title: string
|
||||||
content: string
|
content: string
|
||||||
pages: H5PopupPage[]
|
pages: H5PopupPage[]
|
||||||
priority: number
|
priority: number
|
||||||
frequency: H5PopupFrequency
|
frequency: H5PopupFrequency
|
||||||
enabled: boolean
|
enabled: boolean
|
||||||
action_type: H5PopupActionType
|
action_type: H5PopupActionType | ''
|
||||||
shop_ids: number[]
|
shop_ids: number[]
|
||||||
device_types: string[]
|
device_types: string[]
|
||||||
card_types: H5PopupCardType[]
|
card_types: H5PopupCardType[]
|
||||||
timeRange: string[]
|
timeRange: string[]
|
||||||
} => ({
|
} => ({
|
||||||
id: 0,
|
id: 0,
|
||||||
|
popup_type: 'promotion',
|
||||||
title: '',
|
title: '',
|
||||||
content: '',
|
content: '',
|
||||||
pages: [],
|
pages: [],
|
||||||
@@ -672,6 +640,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const formRules: FormRules = {
|
const formRules: FormRules = {
|
||||||
|
popup_type: [{ required: true, message: '请选择弹窗类型', trigger: 'change' }],
|
||||||
title: [
|
title: [
|
||||||
{ required: true, message: '请输入弹窗标题', trigger: 'blur' },
|
{ required: true, message: '请输入弹窗标题', trigger: 'blur' },
|
||||||
{ min: 1, max: 100, message: '弹窗标题长度为 1~100 字符', trigger: 'blur' }
|
{ min: 1, max: 100, message: '弹窗标题长度为 1~100 字符', trigger: 'blur' }
|
||||||
@@ -720,13 +689,14 @@
|
|||||||
formMode.value = 'edit'
|
formMode.value = 'edit'
|
||||||
Object.assign(form, {
|
Object.assign(form, {
|
||||||
id: row.id,
|
id: row.id,
|
||||||
|
popup_type: row.popup_type,
|
||||||
title: row.title,
|
title: row.title,
|
||||||
content: row.content,
|
content: row.content,
|
||||||
pages: [...(row.pages || [])],
|
pages: [...(row.pages || [])],
|
||||||
priority: row.priority,
|
priority: row.priority,
|
||||||
frequency: row.frequency,
|
frequency: row.frequency,
|
||||||
enabled: row.enabled,
|
enabled: row.enabled,
|
||||||
action_type: (row.action_type || '') as H5PopupActionType,
|
action_type: row.action_type || '',
|
||||||
shop_ids: [...(row.shop_ids || [])],
|
shop_ids: [...(row.shop_ids || [])],
|
||||||
device_types: [...(row.device_types || [])],
|
device_types: [...(row.device_types || [])],
|
||||||
card_types: [...(row.card_types || [])],
|
card_types: [...(row.card_types || [])],
|
||||||
@@ -741,6 +711,7 @@
|
|||||||
if (!(await formRef.value?.validate().catch(() => false))) return
|
if (!(await formRef.value?.validate().catch(() => false))) return
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
const payload = {
|
const payload = {
|
||||||
|
popup_type: form.popup_type,
|
||||||
title: form.title.trim(),
|
title: form.title.trim(),
|
||||||
content: form.content.trim(),
|
content: form.content.trim(),
|
||||||
pages: [...form.pages],
|
pages: [...form.pages],
|
||||||
@@ -749,14 +720,16 @@
|
|||||||
starts_at: form.timeRange[0],
|
starts_at: form.timeRange[0],
|
||||||
ends_at: form.timeRange[1],
|
ends_at: form.timeRange[1],
|
||||||
enabled: form.enabled,
|
enabled: form.enabled,
|
||||||
action_type: form.action_type,
|
|
||||||
shop_ids: [...form.shop_ids],
|
shop_ids: [...form.shop_ids],
|
||||||
device_types: [...form.device_types],
|
device_types: [...form.device_types],
|
||||||
card_types: [...form.card_types]
|
card_types: [...form.card_types]
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (formMode.value === 'create') {
|
if (formMode.value === 'create') {
|
||||||
const res = await H5PopupConfigurationService.createConfiguration(payload)
|
const res = await H5PopupConfigurationService.createConfiguration({
|
||||||
|
...payload,
|
||||||
|
action_type: form.action_type || null
|
||||||
|
})
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
ElMessage.success('创建成功')
|
ElMessage.success('创建成功')
|
||||||
formDialogVisible.value = false
|
formDialogVisible.value = false
|
||||||
@@ -767,7 +740,8 @@
|
|||||||
} else {
|
} else {
|
||||||
const res = await H5PopupConfigurationService.updateConfiguration(form.id, {
|
const res = await H5PopupConfigurationService.updateConfiguration(form.id, {
|
||||||
id: form.id,
|
id: form.id,
|
||||||
...payload
|
...payload,
|
||||||
|
action_type: form.action_type
|
||||||
})
|
})
|
||||||
if (res.code === 0 && res.data) {
|
if (res.code === 0 && res.data) {
|
||||||
ElMessage.success(
|
ElMessage.success(
|
||||||
@@ -796,15 +770,4 @@
|
|||||||
.h5-popup-configuration-page {
|
.h5-popup-configuration-page {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-loading {
|
|
||||||
padding: 24px 0;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.detail-content {
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-break: break-all;
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,34 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<ArtTableFullScreen>
|
<ArtTableFullScreen>
|
||||||
<div class="business-owner-import-page" id="table-full-screen">
|
<div class="business-owner-import-page" id="table-full-screen">
|
||||||
<!-- 导入入口卡片 -->
|
|
||||||
<ElCard shadow="never" class="import-entry-card">
|
|
||||||
<div class="import-entry">
|
|
||||||
<div class="import-tip">
|
|
||||||
<p>通过 CSV 批量交接/清空店铺平台业务员。</p>
|
|
||||||
<p
|
|
||||||
>表头固定:<code>店铺编码,操作类型,业务员登录账号,备注</code>;操作类型仅支持「换绑」或「清空」。</p
|
|
||||||
>
|
|
||||||
<p>支持 UTF-8(可带 BOM),一行失败不影响其他行。</p>
|
|
||||||
</div>
|
|
||||||
<div class="import-actions">
|
|
||||||
<ElButton
|
|
||||||
v-permission="SHOP_BUSINESS_OWNER_PERMISSIONS.importPage"
|
|
||||||
@click="handleDownloadTemplate"
|
|
||||||
>
|
|
||||||
下载模板
|
|
||||||
</ElButton>
|
|
||||||
<ElButton
|
|
||||||
type="primary"
|
|
||||||
v-permission="SHOP_BUSINESS_OWNER_PERMISSIONS.importCreate"
|
|
||||||
@click="importDialogVisible = true"
|
|
||||||
>
|
|
||||||
导入业务员
|
|
||||||
</ElButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ElCard>
|
|
||||||
|
|
||||||
<ElCard shadow="never" class="art-table-card">
|
<ElCard shadow="never" class="art-table-card">
|
||||||
<!-- 表格头部 -->
|
<!-- 表格头部 -->
|
||||||
<ArtTableHeader
|
<ArtTableHeader
|
||||||
@@ -37,20 +9,29 @@
|
|||||||
@refresh="handleRefresh"
|
@refresh="handleRefresh"
|
||||||
>
|
>
|
||||||
<template #left>
|
<template #left>
|
||||||
<ElSelect
|
<div class="header-actions">
|
||||||
v-model="searchForm.status"
|
<ElSelect
|
||||||
placeholder="状态:全部"
|
v-model="searchForm.status"
|
||||||
clearable
|
placeholder="状态:全部"
|
||||||
style="width: 160px"
|
clearable
|
||||||
@change="handleSearch"
|
style="width: 160px"
|
||||||
>
|
@change="handleSearch"
|
||||||
<ElOption
|
>
|
||||||
v-for="option in importStatusOptions"
|
<ElOption
|
||||||
:key="option.value"
|
v-for="option in importStatusOptions"
|
||||||
:label="option.label"
|
:key="option.value"
|
||||||
:value="option.value"
|
:label="option.label"
|
||||||
/>
|
:value="option.value"
|
||||||
</ElSelect>
|
/>
|
||||||
|
</ElSelect>
|
||||||
|
<ElButton
|
||||||
|
type="primary"
|
||||||
|
v-permission="SHOP_BUSINESS_OWNER_PERMISSIONS.importCreate"
|
||||||
|
@click="importDialogVisible = true"
|
||||||
|
>
|
||||||
|
导入业务员
|
||||||
|
</ElButton>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</ArtTableHeader>
|
</ArtTableHeader>
|
||||||
|
|
||||||
@@ -63,8 +44,6 @@
|
|||||||
:currentPage="pagination.page"
|
:currentPage="pagination.page"
|
||||||
:pageSize="pagination.pageSize"
|
:pageSize="pagination.pageSize"
|
||||||
:total="pagination.total"
|
:total="pagination.total"
|
||||||
:actions="getActions"
|
|
||||||
:actionsWidth="120"
|
|
||||||
:marginTop="10"
|
:marginTop="10"
|
||||||
:height="400"
|
:height="400"
|
||||||
@size-change="handleSizeChange"
|
@size-change="handleSizeChange"
|
||||||
@@ -80,7 +59,12 @@
|
|||||||
<!-- 新建导入弹窗 -->
|
<!-- 新建导入弹窗 -->
|
||||||
<ElDialog v-model="importDialogVisible" title="导入业务员" width="560px" destroy-on-close>
|
<ElDialog v-model="importDialogVisible" title="导入业务员" width="560px" destroy-on-close>
|
||||||
<ElAlert type="info" :closable="false" show-icon class="import-alert">
|
<ElAlert type="info" :closable="false" show-icon class="import-alert">
|
||||||
请先下载模板,按模板填写店铺编码与操作类型后再上传 CSV。
|
<template #title>导入说明</template>
|
||||||
|
<div>通过 CSV 批量交接或清空店铺平台业务员。</div>
|
||||||
|
<div>
|
||||||
|
表头固定:<code>店铺编码,操作类型,业务员登录账号,备注</code>;操作类型仅支持「换绑」或「清空」。
|
||||||
|
</div>
|
||||||
|
<div>支持 UTF-8(可带 BOM),一行失败不影响其他行。</div>
|
||||||
</ElAlert>
|
</ElAlert>
|
||||||
<div class="upload-tip-row">
|
<div class="upload-tip-row">
|
||||||
<ElButton @click="handleDownloadTemplate">下载模板</ElButton>
|
<ElButton @click="handleDownloadTemplate">下载模板</ElButton>
|
||||||
@@ -118,11 +102,11 @@
|
|||||||
<!-- 导入结果抽屉 -->
|
<!-- 导入结果抽屉 -->
|
||||||
<ElDrawer
|
<ElDrawer
|
||||||
v-model="detailDrawerVisible"
|
v-model="detailDrawerVisible"
|
||||||
:title="`导入结果 - ${currentTask?.id || ''}`"
|
:title="`导入结果${currentTask?.task_no ? ` - ${currentTask.task_no}` : ''}`"
|
||||||
size="70%"
|
size="820px"
|
||||||
destroy-on-close
|
destroy-on-close
|
||||||
>
|
>
|
||||||
<div v-if="currentTask" class="import-detail">
|
<div v-if="currentTask" v-loading="detailLoading" class="import-detail">
|
||||||
<ElAlert
|
<ElAlert
|
||||||
:type="detailAlertType"
|
:type="detailAlertType"
|
||||||
:closable="false"
|
:closable="false"
|
||||||
@@ -136,19 +120,19 @@
|
|||||||
</ElAlert>
|
</ElAlert>
|
||||||
|
|
||||||
<div class="detail-stats">
|
<div class="detail-stats">
|
||||||
<div class="stat-item">
|
<div class="stat-item stat-item--total">
|
||||||
<span class="stat-label">总数</span>
|
<span class="stat-label">总数</span>
|
||||||
<strong>{{ currentTask.total_count ?? 0 }}</strong>
|
<strong>{{ currentTask.total_count ?? 0 }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item stat-item--success">
|
||||||
<span class="stat-label">成功</span>
|
<span class="stat-label">成功</span>
|
||||||
<strong class="stat-success">{{ currentTask.success_count ?? 0 }}</strong>
|
<strong class="stat-success">{{ currentTask.success_count ?? 0 }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item stat-item--fail">
|
||||||
<span class="stat-label">失败</span>
|
<span class="stat-label">失败</span>
|
||||||
<strong class="stat-fail">{{ currentTask.fail_count ?? 0 }}</strong>
|
<strong class="stat-fail">{{ currentTask.fail_count ?? 0 }}</strong>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item stat-item--status">
|
||||||
<span class="stat-label">状态</span>
|
<span class="stat-label">状态</span>
|
||||||
<strong>{{ currentTask.status_name || importStatusLabel(currentTask.status) }}</strong>
|
<strong>{{ currentTask.status_name || importStatusLabel(currentTask.status) }}</strong>
|
||||||
</div>
|
</div>
|
||||||
@@ -181,6 +165,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-section-title">
|
||||||
|
<span>逐行处理结果</span>
|
||||||
|
<ElTag size="small" type="info">{{ rowItems.length }} 条</ElTag>
|
||||||
|
</div>
|
||||||
<ElTable v-if="rowItems.length" :data="rowItems" border stripe max-height="420">
|
<ElTable v-if="rowItems.length" :data="rowItems" border stripe max-height="420">
|
||||||
<ElTableColumn label="行号" prop="line" width="80" align="center" />
|
<ElTableColumn label="行号" prop="line" width="80" align="center" />
|
||||||
<ElTableColumn label="店铺编码" prop="shop_code" min-width="140" />
|
<ElTableColumn label="店铺编码" prop="shop_code" min-width="140" />
|
||||||
@@ -195,8 +183,8 @@
|
|||||||
<ElTableColumn label="失败原因" prop="reason" min-width="220" show-overflow-tooltip />
|
<ElTableColumn label="失败原因" prop="reason" min-width="220" show-overflow-tooltip />
|
||||||
</ElTable>
|
</ElTable>
|
||||||
<ElEmpty
|
<ElEmpty
|
||||||
v-else-if="!detailLoading && !rowItems.length && currentTask.status === 4"
|
v-else-if="!detailLoading"
|
||||||
description="任务级失败,无行级明细"
|
:description="currentTask.status === 4 ? '任务级失败,无行级明细' : '暂无逐行处理结果'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</ElDrawer>
|
</ElDrawer>
|
||||||
@@ -211,14 +199,11 @@
|
|||||||
import { ShopService, StorageService } from '@/api/modules'
|
import { ShopService, StorageService } from '@/api/modules'
|
||||||
import type { BusinessOwnerImportDetail } from '@/types/api'
|
import type { BusinessOwnerImportDetail } from '@/types/api'
|
||||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||||
import { useAuth } from '@/composables/useAuth'
|
|
||||||
import { formatDateTime } from '@/utils/business/format'
|
import { formatDateTime } from '@/utils/business/format'
|
||||||
import { SHOP_BUSINESS_OWNER_PERMISSIONS } from '@/config/constants'
|
import { SHOP_BUSINESS_OWNER_PERMISSIONS } from '@/config/constants'
|
||||||
|
|
||||||
defineOptions({ name: 'BusinessOwnerImport' })
|
defineOptions({ name: 'BusinessOwnerImport' })
|
||||||
|
|
||||||
const { hasAuth } = useAuth()
|
|
||||||
|
|
||||||
const importStatusOptions = [
|
const importStatusOptions = [
|
||||||
{ label: '待处理', value: 1 },
|
{ label: '待处理', value: 1 },
|
||||||
{ label: '处理中', value: 2 },
|
{ label: '处理中', value: 2 },
|
||||||
@@ -244,7 +229,6 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
const columnOptions = [
|
const columnOptions = [
|
||||||
{ label: '任务ID', prop: 'id', width: 90 },
|
|
||||||
{ label: '任务编号', prop: 'task_no', width: 160 },
|
{ label: '任务编号', prop: 'task_no', width: 160 },
|
||||||
{ label: '文件名', prop: 'file_name', minWidth: 160, showOverflowTooltip: true },
|
{ label: '文件名', prop: 'file_name', minWidth: 160, showOverflowTooltip: true },
|
||||||
{ label: '创建人', prop: 'creator_name', width: 100 },
|
{ label: '创建人', prop: 'creator_name', width: 100 },
|
||||||
@@ -258,16 +242,23 @@
|
|||||||
]
|
]
|
||||||
|
|
||||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||||
{
|
|
||||||
prop: 'id',
|
|
||||||
label: '任务ID',
|
|
||||||
width: 90
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
prop: 'task_no',
|
prop: 'task_no',
|
||||||
label: '任务编号',
|
label: '任务编号',
|
||||||
width: 160,
|
width: 160,
|
||||||
showOverflowTooltip: true
|
showOverflowTooltip: true,
|
||||||
|
formatter: (row: BusinessOwnerImportDetail) =>
|
||||||
|
h(
|
||||||
|
'span',
|
||||||
|
{
|
||||||
|
style: 'color: var(--el-color-primary); cursor: pointer; text-decoration: underline;',
|
||||||
|
onClick: (event: MouseEvent) => {
|
||||||
|
event.stopPropagation()
|
||||||
|
openDetail(row)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
row.task_no || '--'
|
||||||
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
prop: 'file_name',
|
prop: 'file_name',
|
||||||
@@ -318,17 +309,6 @@
|
|||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const getActions = (row: BusinessOwnerImportDetail) => {
|
|
||||||
if (!hasAuth(SHOP_BUSINESS_OWNER_PERMISSIONS.importPage)) return []
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
label: '查看结果',
|
|
||||||
handler: () => openDetail(row),
|
|
||||||
type: 'primary' as const
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -337,8 +317,13 @@
|
|||||||
page_size: pagination.pageSize,
|
page_size: pagination.pageSize,
|
||||||
status: searchForm.status
|
status: searchForm.status
|
||||||
})
|
})
|
||||||
tableData.value = res.data?.items || []
|
if (res.code === 0 && res.data) {
|
||||||
pagination.total = res.data?.total || 0
|
const { items, total, page, size } = res.data
|
||||||
|
tableData.value = items || []
|
||||||
|
pagination.total = total || 0
|
||||||
|
pagination.page = page ?? pagination.page
|
||||||
|
pagination.pageSize = size ?? pagination.pageSize
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// 错误提示由请求层统一处理
|
// 错误提示由请求层统一处理
|
||||||
} finally {
|
} finally {
|
||||||
@@ -537,38 +522,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.import-entry-card {
|
.header-actions {
|
||||||
margin-bottom: 12px;
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
.import-entry {
|
align-items: center;
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: 16px;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.import-tip {
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
line-height: 1.8;
|
|
||||||
|
|
||||||
p {
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
code {
|
|
||||||
padding: 2px 6px;
|
|
||||||
font-size: 12px;
|
|
||||||
background: var(--el-fill-color-light);
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.import-actions {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.import-alert {
|
.import-alert {
|
||||||
@@ -596,8 +553,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.import-detail {
|
.import-detail {
|
||||||
|
padding: 0 4px 20px;
|
||||||
|
|
||||||
.detail-alert {
|
.detail-alert {
|
||||||
margin-bottom: 16px;
|
margin-bottom: 16px;
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-error {
|
.detail-error {
|
||||||
@@ -607,14 +567,35 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.detail-stats {
|
.detail-stats {
|
||||||
display: flex;
|
display: grid;
|
||||||
gap: 24px;
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
margin-bottom: 16px;
|
gap: 12px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
.stat-item {
|
.stat-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 4px;
|
gap: 6px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 14px 16px;
|
||||||
|
background: var(--el-fill-color-light);
|
||||||
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
|
&--success {
|
||||||
|
background: var(--el-color-success-light-9);
|
||||||
|
border-color: var(--el-color-success-light-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--fail {
|
||||||
|
background: var(--el-color-danger-light-9);
|
||||||
|
border-color: var(--el-color-danger-light-7);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--status {
|
||||||
|
background: var(--el-color-primary-light-9);
|
||||||
|
border-color: var(--el-color-primary-light-7);
|
||||||
|
}
|
||||||
|
|
||||||
.stat-label {
|
.stat-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -622,7 +603,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
font-size: 18px;
|
font-size: 22px;
|
||||||
|
line-height: 1.2;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stat-success {
|
.stat-success {
|
||||||
@@ -638,11 +620,12 @@
|
|||||||
.detail-meta {
|
.detail-meta {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
|
||||||
gap: 12px 24px;
|
gap: 14px 24px;
|
||||||
margin-bottom: 16px;
|
margin-bottom: 20px;
|
||||||
padding: 12px 16px;
|
padding: 16px;
|
||||||
background: var(--el-fill-color-light);
|
background: var(--el-fill-color-lighter);
|
||||||
border-radius: 6px;
|
border: 1px solid var(--el-border-color-lighter);
|
||||||
|
border-radius: 10px;
|
||||||
|
|
||||||
.meta-item {
|
.meta-item {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -662,5 +645,15 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.detail-section-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user