This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
*/
|
||||
|
||||
import { BaseService } from '../BaseService'
|
||||
import { getNewLoginPasswordError } from '@/utils/validation/loginPassword'
|
||||
import type {
|
||||
PlatformAccount,
|
||||
AccountQueryParams,
|
||||
@@ -48,7 +49,13 @@ export class AccountService extends BaseService {
|
||||
id: number,
|
||||
data: Partial<CreatePlatformAccountParams>
|
||||
): Promise<BaseResponse> {
|
||||
return this.update(`/api/admin/accounts/${id}`, data)
|
||||
const payload = { ...data }
|
||||
if (payload.password === '') delete payload.password
|
||||
if (payload.password !== undefined) {
|
||||
const error = getNewLoginPasswordError(payload.password)
|
||||
if (error) return Promise.reject(new Error(error))
|
||||
}
|
||||
return this.update(`/api/admin/accounts/${id}`, payload)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -106,7 +113,9 @@ export class AccountService extends BaseService {
|
||||
* @param password 新密码
|
||||
*/
|
||||
static updateAccountPassword(id: number, password: string): Promise<BaseResponse> {
|
||||
return this.put<BaseResponse>(`/api/admin/accounts/${id}/password`, { password })
|
||||
const error = getNewLoginPasswordError(password)
|
||||
if (error) return Promise.reject(new Error(error))
|
||||
return this.put<BaseResponse>(`/api/admin/accounts/${id}/password`, { new_password: password })
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -138,8 +138,15 @@ export class ExchangeService extends BaseService {
|
||||
* POST /api/admin/exchanges/{id}/complete
|
||||
* @param id 换货单ID
|
||||
*/
|
||||
static completeExchange(id: number): Promise<BaseResponse<ExchangeResponse>> {
|
||||
return this.post<BaseResponse<ExchangeResponse>>(`/api/admin/exchanges/${id}/complete`, {})
|
||||
static completeExchange(
|
||||
id: number,
|
||||
config?: Record<string, any>
|
||||
): Promise<BaseResponse<ExchangeResponse>> {
|
||||
return this.post<BaseResponse<ExchangeResponse>>(
|
||||
`/api/admin/exchanges/${id}/complete`,
|
||||
{},
|
||||
config
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
import { BaseService } from '../BaseService'
|
||||
import type {
|
||||
Order,
|
||||
OrderDetail,
|
||||
OrderQueryParams,
|
||||
OrderListResponse,
|
||||
CreateOrderRequest,
|
||||
@@ -25,8 +25,8 @@ export class OrderService extends BaseService {
|
||||
* 获取订单详情
|
||||
* @param id 订单ID
|
||||
*/
|
||||
static getOrderById(id: number): Promise<BaseResponse<Order>> {
|
||||
return this.getOne<Order>(`/api/admin/orders/${id}`)
|
||||
static getOrderById(id: number): Promise<BaseResponse<OrderDetail>> {
|
||||
return this.getOne<OrderDetail>(`/api/admin/orders/${id}`)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -20,17 +20,23 @@
|
||||
v-model="formData.old_password"
|
||||
type="password"
|
||||
autocomplete="current-password"
|
||||
maxlength="32"
|
||||
show-password
|
||||
:disabled="submitting"
|
||||
/>
|
||||
</ElFormItem>
|
||||
<ElAlert
|
||||
:title="t('passwordChange.passwordRule')"
|
||||
type="info"
|
||||
:closable="false"
|
||||
style="margin-bottom: 16px"
|
||||
/>
|
||||
<ElFormItem prop="new_password" :label="t('passwordChange.newPassword')">
|
||||
<ElInput
|
||||
v-model="formData.new_password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
maxlength="32"
|
||||
maxlength="12"
|
||||
:placeholder="t('passwordChange.passwordRule')"
|
||||
show-password
|
||||
:disabled="submitting"
|
||||
/>
|
||||
@@ -40,7 +46,8 @@
|
||||
v-model="formData.confirm_password"
|
||||
type="password"
|
||||
autocomplete="new-password"
|
||||
maxlength="32"
|
||||
maxlength="12"
|
||||
:placeholder="t('passwordChange.passwordRule')"
|
||||
show-password
|
||||
:disabled="submitting"
|
||||
@keyup.enter="handleSubmit"
|
||||
@@ -61,12 +68,13 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { ElAlert, ElMessage } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import { AuthService } from '@/api/modules'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { normalizeApiError } from '@/utils/business/apiError'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { createNewLoginPasswordRule } from '@/utils/validation/loginPassword'
|
||||
|
||||
defineOptions({ name: 'ChangePasswordDialog' })
|
||||
|
||||
@@ -90,16 +98,15 @@
|
||||
|
||||
const formRules = computed<FormRules>(() => ({
|
||||
old_password: [
|
||||
{ required: true, message: t('passwordChange.oldPasswordRequired'), trigger: 'blur' },
|
||||
{ min: 8, max: 32, message: t('passwordChange.passwordLength'), trigger: 'blur' }
|
||||
{ required: true, message: t('passwordChange.oldPasswordRequired'), trigger: 'blur' }
|
||||
],
|
||||
new_password: [
|
||||
{ required: true, message: t('passwordChange.newPasswordRequired'), trigger: 'blur' },
|
||||
{ min: 8, max: 32, message: t('passwordChange.passwordLength'), trigger: 'blur' }
|
||||
createNewLoginPasswordRule(t('passwordChange.passwordRule'))
|
||||
],
|
||||
confirm_password: [
|
||||
{ required: true, message: t('passwordChange.confirmPasswordRequired'), trigger: 'blur' },
|
||||
{ min: 8, max: 32, message: t('passwordChange.passwordLength'), trigger: 'blur' },
|
||||
createNewLoginPasswordRule(t('passwordChange.passwordRule')),
|
||||
{
|
||||
validator: (_rule, value: string, callback) => {
|
||||
if (value && value !== formData.new_password) {
|
||||
@@ -139,7 +146,7 @@
|
||||
})
|
||||
|
||||
if (response.code !== 0) {
|
||||
ElMessage.error(response.msg || t('passwordChange.failure'))
|
||||
ElMessage.error(response.msg || response.message || t('passwordChange.failure'))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -155,3 +162,11 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
:deep(.el-form-item__error) {
|
||||
position: static;
|
||||
width: 100%;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -19,7 +19,12 @@ export const WITHDRAWAL_STATUS_OPTIONS = [
|
||||
{ label: '待审核', value: WithdrawalStatus.PENDING, type: 'warning' as const },
|
||||
{ label: '已通过', value: WithdrawalStatus.APPROVED, type: 'success' as const },
|
||||
{ label: '已拒绝', value: WithdrawalStatus.REJECTED, type: 'danger' as const },
|
||||
{ label: '已到账', value: WithdrawalStatus.COMPLETED, type: 'success' as const }
|
||||
{ label: '已到账', value: WithdrawalStatus.COMPLETED, type: 'success' as const },
|
||||
{
|
||||
label: '审批提交失败',
|
||||
value: WithdrawalStatus.APPROVAL_SUBMISSION_FAILED,
|
||||
type: 'danger' as const
|
||||
}
|
||||
]
|
||||
|
||||
// 支付方式选项
|
||||
@@ -36,9 +41,22 @@ export const WithdrawalStatusMap = {
|
||||
1: { label: '待审核', type: 'warning' as const, color: '#E6A23C' },
|
||||
2: { label: '已通过', type: 'success' as const, color: '#67C23A' },
|
||||
3: { label: '已拒绝', type: 'danger' as const, color: '#F56C6C' },
|
||||
4: { label: '已到账', type: 'success' as const, color: '#67C23A' }
|
||||
4: { label: '已到账', type: 'success' as const, color: '#67C23A' },
|
||||
5: { label: '审批提交失败', type: 'danger' as const, color: '#F56C6C' }
|
||||
}
|
||||
|
||||
export const WITHDRAWAL_QUALIFICATION_APPROVAL_FAILED = 4
|
||||
export const WITHDRAWAL_QUALIFICATION_STATUS_OPTIONS = [
|
||||
{ label: '待审批', value: 0 },
|
||||
{ label: '已通过', value: 1 },
|
||||
{ label: '已驳回', value: 2 },
|
||||
{ label: '已作废', value: 3 },
|
||||
{ label: '审批提交失败', value: WITHDRAWAL_QUALIFICATION_APPROVAL_FAILED }
|
||||
]
|
||||
|
||||
export const canResubmitWithdrawal = (status: number): boolean =>
|
||||
status === WithdrawalStatus.REJECTED || status === WithdrawalStatus.APPROVAL_SUBMISSION_FAILED
|
||||
|
||||
// ========== 佣金状态映射 ==========
|
||||
|
||||
// 佣金状态映射 (1:已冻结, 2:解冻中, 3:已发放, 4:已失效, 99:待人工修正)
|
||||
@@ -134,10 +152,7 @@ export const COMMISSION_RECORD_STATUS_OPTIONS = [
|
||||
]
|
||||
|
||||
// 是否回溯明细行(source 与 status 双判断,兼容未返回 source 的响应)
|
||||
export function isCommissionClawbackRecord(record: {
|
||||
source?: string
|
||||
status?: number
|
||||
}): boolean {
|
||||
export function isCommissionClawbackRecord(record: { source?: string; status?: number }): boolean {
|
||||
return record.source === 'clawback' || record.status === CommissionStatus.CLAWBACK
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,8 @@ export const RefundStatusNameMap: Record<RefundStatus, string> = {
|
||||
[RefundStatus.REJECTED]: '已拒绝',
|
||||
[RefundStatus.RETURNED]: '已退回',
|
||||
[RefundStatus.REFUNDING]: '原路退款处理中',
|
||||
[RefundStatus.REFUND_FAILED]: '原路退款失败'
|
||||
[RefundStatus.REFUND_FAILED]: '原路退款失败',
|
||||
[RefundStatus.APPROVAL_SUBMISSION_FAILED]: '审批提交失败'
|
||||
}
|
||||
|
||||
/** 退款状态标签类型映射 */
|
||||
@@ -27,7 +28,8 @@ export const RefundStatusTypeMap: Record<RefundStatus, 'warning' | 'success' | '
|
||||
[RefundStatus.REJECTED]: 'danger',
|
||||
[RefundStatus.RETURNED]: 'info',
|
||||
[RefundStatus.REFUNDING]: 'warning',
|
||||
[RefundStatus.REFUND_FAILED]: 'danger'
|
||||
[RefundStatus.REFUND_FAILED]: 'danger',
|
||||
[RefundStatus.APPROVAL_SUBMISSION_FAILED]: 'danger'
|
||||
}
|
||||
|
||||
/** 退款方式名称映射 */
|
||||
@@ -73,7 +75,12 @@ export const canResubmitRefund = (item: {
|
||||
anomaly_flag?: number
|
||||
}): boolean => {
|
||||
if (!item.status) return false
|
||||
if (item.status === RefundStatus.REJECTED || item.status === RefundStatus.RETURNED) return true
|
||||
if (
|
||||
item.status === RefundStatus.REJECTED ||
|
||||
item.status === RefundStatus.RETURNED ||
|
||||
item.status === RefundStatus.APPROVAL_SUBMISSION_FAILED
|
||||
)
|
||||
return true
|
||||
if (item.status === RefundStatus.REFUND_FAILED) return item.anomaly_flag !== 1
|
||||
return false
|
||||
}
|
||||
@@ -85,3 +92,9 @@ export const canManualApproveRefund = (item: {
|
||||
status?: RefundStatus
|
||||
approval_instance_id?: number | null
|
||||
}): boolean => item.status === RefundStatus.PENDING && !item.approval_instance_id
|
||||
|
||||
export const canRecoverRefundApproval = (item: {
|
||||
status?: RefundStatus
|
||||
approval_recoverable?: boolean
|
||||
}): boolean =>
|
||||
item.status !== RefundStatus.APPROVAL_SUBMISSION_FAILED && item.approval_recoverable === true
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
"oldPasswordRequired": "Please enter your current password",
|
||||
"newPasswordRequired": "Please enter a new password",
|
||||
"confirmPasswordRequired": "Please enter the new password again",
|
||||
"passwordLength": "Password must be 8-32 characters",
|
||||
"passwordLength": "Password must be 8-12 characters",
|
||||
"passwordRule": "8-12 characters with uppercase, lowercase and digits; no special characters",
|
||||
"passwordMismatch": "The new passwords do not match",
|
||||
"submit": "Change password",
|
||||
"success": "Password changed successfully",
|
||||
|
||||
@@ -27,7 +27,8 @@
|
||||
"oldPasswordRequired": "请输入当前密码",
|
||||
"newPasswordRequired": "请输入新密码",
|
||||
"confirmPasswordRequired": "请再次输入新密码",
|
||||
"passwordLength": "密码长度需为8-32位",
|
||||
"passwordLength": "密码长度需为8-12位",
|
||||
"passwordRule": "8~12位,必须包含大写字母、小写字母和数字,不允许特殊符号",
|
||||
"passwordMismatch": "两次输入的新密码不一致",
|
||||
"submit": "确认修改",
|
||||
"success": "密码修改成功",
|
||||
|
||||
@@ -124,7 +124,7 @@ export interface AgentQueryParams extends PaginationParams {
|
||||
// 创建账号参数(匹配后端 ModelCreateAccountRequest)
|
||||
export interface CreatePlatformAccountParams {
|
||||
username: string // 用户名 (3-50字符)
|
||||
password: string // 密码 (8-32字符)
|
||||
password: string // 8~12位,必须包含大写、小写和数字,禁止特殊符号
|
||||
phone: string // 手机号 (11位)
|
||||
user_type: number // 用户类型 (1:超级管理员, 2:平台用户, 3:代理账号, 4:企业账号)
|
||||
enterprise_id?: number | null // 关联企业ID(企业账号必填)
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
export interface AgentDistributionRegistrationQueryParams {
|
||||
page?: number
|
||||
page_size?: number
|
||||
status?: 0 | 1 | 2
|
||||
status?: 0 | 1 | 2 | 3
|
||||
start_time?: string
|
||||
end_time?: string
|
||||
decided_start_time?: string
|
||||
@@ -54,3 +54,17 @@ export interface RecoverAgentDistributionRegistrationResponse {
|
||||
id: number
|
||||
message?: string
|
||||
}
|
||||
|
||||
export const AGENT_REGISTRATION_APPROVAL_FAILED_STATUS = 3
|
||||
|
||||
export const canRecoverAgentRegistration = (
|
||||
item?: AgentDistributionRegistrationItem | null
|
||||
): boolean =>
|
||||
!!item && item.status !== AGENT_REGISTRATION_APPROVAL_FAILED_STATUS && item.recoverable === true
|
||||
|
||||
export const getAgentRegistrationStatusName = (item: AgentDistributionRegistrationItem): string =>
|
||||
item.status_name ||
|
||||
({ 0: '待审批', 1: '已通过', 2: '已驳回', 3: '审批提交失败' } as Record<number, string>)[
|
||||
item.status
|
||||
] ||
|
||||
String(item.status)
|
||||
|
||||
@@ -9,7 +9,8 @@ export enum AgentRechargeStatus {
|
||||
COMPLETED = 3, // 已完成
|
||||
CLOSED = 4, // 已关闭
|
||||
REFUNDED = 5, // 已退款
|
||||
REJECTED = 6 // 已驳回
|
||||
REJECTED = 6, // 已驳回
|
||||
APPROVAL_SUBMISSION_FAILED = 7 // 审批提交失败(终态)
|
||||
}
|
||||
|
||||
// 支付方式
|
||||
|
||||
@@ -137,7 +137,8 @@ export enum WithdrawalStatus {
|
||||
PENDING = 1, // 待审核
|
||||
APPROVED = 2, // 已通过
|
||||
REJECTED = 3, // 已拒绝
|
||||
COMPLETED = 4 // 已到账
|
||||
COMPLETED = 4, // 已到账
|
||||
APPROVAL_SUBMISSION_FAILED = 5 // 审批提交失败(终态)
|
||||
}
|
||||
|
||||
// 佣金类型
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
export interface BaseResponse<T = any> {
|
||||
code: number
|
||||
msg: string
|
||||
message?: string
|
||||
data: T
|
||||
timestamp?: string
|
||||
}
|
||||
|
||||
@@ -183,7 +183,7 @@ export type CloseEmployeeCollectionBillResult = EmployeeCollectionBill
|
||||
|
||||
// ========== 核销申请 ==========
|
||||
|
||||
/** 申请状态:0 审批中 / 1 已通过 / 2 已驳回 / 3 已关闭 */
|
||||
/** 申请状态:0 审批中 / 1 已通过 / 2 已驳回 / 3 已关闭 / 4 审批提交失败 */
|
||||
export type EmployeeCollectionApplicationStatus = number
|
||||
|
||||
/** 审批尝试记录(历史材料不被覆盖) */
|
||||
|
||||
@@ -10,6 +10,28 @@ export enum PaymentStatus {
|
||||
REFUNDED = 4 // 已退款
|
||||
}
|
||||
|
||||
// 支付单状态与订单支付状态数值不同,必须独立处理。
|
||||
export enum OrderPaymentRecordStatus {
|
||||
PENDING = 0,
|
||||
PAID = 1,
|
||||
FAILED = 2,
|
||||
REFUNDED = 3
|
||||
}
|
||||
|
||||
export interface OrderPaymentRecord {
|
||||
payment_no: string
|
||||
payment_method: 'wechat' | 'alipay'
|
||||
payment_channel: string
|
||||
payment_merchant_name: string
|
||||
third_party_trade_no: string
|
||||
amount: number // 分
|
||||
status: OrderPaymentRecordStatus
|
||||
status_name: string
|
||||
created_at: string
|
||||
paid_at: string | null
|
||||
expire_at: string | null
|
||||
}
|
||||
|
||||
// 订单类型
|
||||
export type OrderType = 'single_card' | 'device'
|
||||
|
||||
@@ -68,6 +90,9 @@ export interface Order {
|
||||
payment_status: PaymentStatus
|
||||
payment_status_text: string
|
||||
payment_method: OrderPaymentMethod
|
||||
third_party_trade_no: string
|
||||
payment_channel: string
|
||||
payment_merchant_name: string
|
||||
total_amount: number // 订单总金额(分)
|
||||
actual_paid_amount?: number // 实付金额(分)
|
||||
paid_at: string | null
|
||||
@@ -97,6 +122,10 @@ export interface Order {
|
||||
updated_at: string
|
||||
}
|
||||
|
||||
export interface OrderDetail extends Order {
|
||||
payments: OrderPaymentRecord[]
|
||||
}
|
||||
|
||||
// 订单查询参数
|
||||
export interface OrderQueryParams {
|
||||
page?: number
|
||||
|
||||
@@ -19,7 +19,6 @@ export interface PackageTrafficAlertRuleItem {
|
||||
id: number // 预警规则ID
|
||||
package_id: number // 套餐商品ID
|
||||
package_name: string // 套餐名称
|
||||
real_data_mb: number // 套餐商品当前真流量额度(MB),仅用于配置校验展示,不作为预警分母
|
||||
threshold_percent: number // 真流量预警阈值百分比(1~100,允许两位小数)
|
||||
enabled: boolean // 是否启用
|
||||
enabled_name: string // 启用状态名称(中文)
|
||||
@@ -90,6 +89,9 @@ export interface PackageTrafficAlertRecordItem {
|
||||
asset_type?: PackageTrafficAlertAssetType | null // 资产类型快照
|
||||
asset_identifier?: string | null // 资产或卡标识快照
|
||||
threshold_percent?: number // 触发阈值快照
|
||||
threshold_mb?: number | null // 触发时阈值流量(MB),主套餐与加油包汇总
|
||||
used_mb?: number | null // 触发时已用流量(MB),含加油包
|
||||
limit_mb?: number | null // 触发时总额度(MB),含加油包
|
||||
triggered_at?: string // 触发时间
|
||||
notification_status?: PackageTrafficAlertNotificationStatus // 通知投递结果
|
||||
notification_status_name?: string // 通知投递结果名称
|
||||
|
||||
@@ -9,7 +9,8 @@ export enum RefundStatus {
|
||||
REJECTED = 3, // 已拒绝
|
||||
RETURNED = 4, // 已退回
|
||||
REFUNDING = 5, // 原路退款处理中
|
||||
REFUND_FAILED = 6 // 原路退款失败
|
||||
REFUND_FAILED = 6, // 原路退款失败
|
||||
APPROVAL_SUBMISSION_FAILED = 7 // 审批提交失败(终态)
|
||||
}
|
||||
|
||||
// 退款方式
|
||||
|
||||
21
src/types/components.d.ts
vendored
21
src/types/components.d.ts
vendored
@@ -92,27 +92,16 @@ declare module 'vue' {
|
||||
CreateRefundDialog: typeof import('./../components/business/CreateRefundDialog.vue')['default']
|
||||
CustomerAccountDialog: typeof import('./../components/business/CustomerAccountDialog.vue')['default']
|
||||
DetailPage: typeof import('./../components/common/DetailPage.vue')['default']
|
||||
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||
ElButton: typeof import('element-plus/es')['ElButton']
|
||||
ElCalendar: typeof import('element-plus/es')['ElCalendar']
|
||||
ElCard: typeof import('element-plus/es')['ElCard']
|
||||
ElCascader: typeof import('element-plus/es')['ElCascader']
|
||||
ElCheckbox: typeof import('element-plus/es')['ElCheckbox']
|
||||
ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup']
|
||||
ElCol: typeof import('element-plus/es')['ElCol']
|
||||
ElCollapse: typeof import('element-plus/es')['ElCollapse']
|
||||
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
|
||||
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
|
||||
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
|
||||
ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
|
||||
ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
|
||||
ElDialog: typeof import('element-plus/es')['ElDialog']
|
||||
ElDivider: typeof import('element-plus/es')['ElDivider']
|
||||
ElDrawer: typeof import('element-plus/es')['ElDrawer']
|
||||
ElDropdown: typeof import('element-plus/es')['ElDropdown']
|
||||
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
|
||||
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
|
||||
ElEmpty: typeof import('element-plus/es')['ElEmpty']
|
||||
ElForm: typeof import('element-plus/es')['ElForm']
|
||||
ElFormItem: typeof import('element-plus/es')['ElFormItem']
|
||||
@@ -125,26 +114,16 @@ declare module 'vue' {
|
||||
ElOption: typeof import('element-plus/es')['ElOption']
|
||||
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||
ElPopover: typeof import('element-plus/es')['ElPopover']
|
||||
ElProgress: typeof import('element-plus/es')['ElProgress']
|
||||
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||
ElResult: typeof import('element-plus/es')['ElResult']
|
||||
ElRow: typeof import('element-plus/es')['ElRow']
|
||||
ElScrollbar: typeof import('element-plus/es')['ElScrollbar']
|
||||
ElSelect: typeof import('element-plus/es')['ElSelect']
|
||||
ElSkeleton: typeof import('element-plus/es')['ElSkeleton']
|
||||
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
|
||||
ElSwitch: typeof import('element-plus/es')['ElSwitch']
|
||||
ElTable: typeof import('element-plus/es')['ElTable']
|
||||
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
|
||||
ElTabPane: typeof import('element-plus/es')['ElTabPane']
|
||||
ElTabs: typeof import('element-plus/es')['ElTabs']
|
||||
ElTag: typeof import('element-plus/es')['ElTag']
|
||||
ElTimeline: typeof import('element-plus/es')['ElTimeline']
|
||||
ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem']
|
||||
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
|
||||
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||
ElWatermark: typeof import('element-plus/es')['ElWatermark']
|
||||
ExportTaskCreateDialog: typeof import('./../components/business/ExportTaskCreateDialog.vue')['default']
|
||||
|
||||
@@ -23,7 +23,7 @@ export const normalizeApiError = (error: unknown): NormalizedApiError => {
|
||||
const response = getResponse(error)
|
||||
const status = response?.status
|
||||
const code = response?.data?.code
|
||||
const backendMessage = response?.data?.msg
|
||||
const backendMessage = response?.data?.msg || response?.data?.message
|
||||
const timeout = axios.isAxiosError(error) && error.code === 'ECONNABORTED'
|
||||
|
||||
if (timeout) {
|
||||
|
||||
24
src/utils/business/exchangeError.ts
Normal file
24
src/utils/business/exchangeError.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
type BusinessFailure = {
|
||||
code?: number | string
|
||||
msg?: string
|
||||
message?: string
|
||||
data?: { phone?: unknown }
|
||||
}
|
||||
|
||||
const getBusinessFailure = (value: unknown): BusinessFailure | undefined => {
|
||||
if (!value || typeof value !== 'object') return undefined
|
||||
const candidate = value as { response?: { data?: BusinessFailure } } & BusinessFailure
|
||||
return candidate.response?.data || candidate
|
||||
}
|
||||
|
||||
export const getExchangePhoneConflictMessage = (value: unknown): string | undefined => {
|
||||
const failure = getBusinessFailure(value)
|
||||
if (Number(failure?.code) !== 1202) return undefined
|
||||
const phone = typeof failure?.data?.phone === 'string' ? failure.data.phone.trim() : ''
|
||||
return `新资产已绑定手机号${phone ? ` ${phone}` : ''},请先到『手机号资产关联』解除后再提交`
|
||||
}
|
||||
|
||||
export const getExchangeBusinessErrorMessage = (value: unknown): string | undefined => {
|
||||
const failure = getBusinessFailure(value)
|
||||
return failure?.msg || failure?.message
|
||||
}
|
||||
29
src/utils/business/orderPayment.ts
Normal file
29
src/utils/business/orderPayment.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { OrderPaymentRecordStatus } from '@/types/api/order'
|
||||
|
||||
const CHANNEL_NAMES: Record<string, string> = {
|
||||
wechat: '微信直连 v3',
|
||||
wechat_v2: '微信直连 v2',
|
||||
fuiou: '富友'
|
||||
}
|
||||
|
||||
const PAYMENT_STATUS_NAMES: Record<OrderPaymentRecordStatus, string> = {
|
||||
[OrderPaymentRecordStatus.PENDING]: '待支付',
|
||||
[OrderPaymentRecordStatus.PAID]: '已支付',
|
||||
[OrderPaymentRecordStatus.FAILED]: '已失败',
|
||||
[OrderPaymentRecordStatus.REFUNDED]: '已退款'
|
||||
}
|
||||
|
||||
export const getOrderPaymentChannelName = (channel?: string | null): string =>
|
||||
channel ? CHANNEL_NAMES[channel] || channel : '-'
|
||||
|
||||
export const getOrderPaymentRecordStatusName = (
|
||||
status: OrderPaymentRecordStatus,
|
||||
name?: string | null
|
||||
): string => name || PAYMENT_STATUS_NAMES[status] || String(status)
|
||||
|
||||
export const getOrderPaymentRecordTagType = (status: OrderPaymentRecordStatus) => {
|
||||
if (status === OrderPaymentRecordStatus.PAID) return 'success'
|
||||
if (status === OrderPaymentRecordStatus.FAILED) return 'danger'
|
||||
if (status === OrderPaymentRecordStatus.PENDING) return 'warning'
|
||||
return 'info'
|
||||
}
|
||||
3
src/utils/business/shopBusinessOwnerImport.ts
Normal file
3
src/utils/business/shopBusinessOwnerImport.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
// UTF-8 BOM 确保 Excel 打开中文 CSV 时正确识别编码。
|
||||
export const SHOP_BUSINESS_OWNER_CSV_TEMPLATE =
|
||||
'\uFEFF店铺编码,操作类型,平台登录账号,备注\r\n,仅支持换绑或清空,换绑填写平台用户登录手机号(清空留空),\r\n'
|
||||
2
src/utils/business/trafficAlert.ts
Normal file
2
src/utils/business/trafficAlert.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export const formatTrafficAlertMb = (value?: number | null): string =>
|
||||
value == null || !Number.isFinite(value) ? '-' : value.toFixed(2)
|
||||
24
src/utils/validation/loginPassword.ts
Normal file
24
src/utils/validation/loginPassword.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { FormItemRule } from 'element-plus'
|
||||
|
||||
export const NEW_LOGIN_PASSWORD_HINT = '8~12位,必须包含大写字母、小写字母和数字,不允许特殊符号'
|
||||
|
||||
export const getNewLoginPasswordError = (value: unknown): string | undefined => {
|
||||
if (typeof value !== 'string' || value.length < 8 || value.length > 12) {
|
||||
return '密码长度必须为8~12位'
|
||||
}
|
||||
if (/[^A-Za-z0-9]/.test(value)) return '密码只允许英文字母和数字,不允许特殊符号、空格或中文'
|
||||
if (!/[A-Z]/.test(value) || !/[a-z]/.test(value) || !/[0-9]/.test(value)) {
|
||||
return '密码必须同时包含大写字母、小写字母和数字'
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
|
||||
// 必填由调用表单决定,空值不妨碍不修改密码的账号编辑。
|
||||
export const createNewLoginPasswordRule = (message?: string): FormItemRule => ({
|
||||
validator: (_rule, value, callback) => {
|
||||
if (value === '' || value === undefined || value === null) return callback()
|
||||
const error = getNewLoginPasswordError(value)
|
||||
callback(error ? new Error(message || error) : undefined)
|
||||
},
|
||||
trigger: 'blur'
|
||||
})
|
||||
@@ -56,7 +56,8 @@
|
||||
<ElInput
|
||||
v-model="formData.password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
:placeholder="NEW_LOGIN_PASSWORD_HINT"
|
||||
maxlength="12"
|
||||
show-password
|
||||
/>
|
||||
</ElFormItem>
|
||||
@@ -307,6 +308,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
createNewLoginPasswordRule,
|
||||
NEW_LOGIN_PASSWORD_HINT
|
||||
} from '@/utils/validation/loginPassword'
|
||||
import { h, onActivated } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import {
|
||||
@@ -1043,7 +1048,7 @@
|
||||
username: [{ required: true, message: '请输入账号名称', trigger: 'blur' }],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 8, max: 32, message: '密码长度为 8-32 个字符', trigger: 'blur' }
|
||||
createNewLoginPasswordRule()
|
||||
],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
@@ -1101,6 +1106,10 @@
|
||||
}
|
||||
|
||||
const createRes = await AccountService.createAccount(data)
|
||||
if (createRes.code !== 0) {
|
||||
ElMessage.error(createRes.msg || createRes.message || '账号创建失败')
|
||||
return
|
||||
}
|
||||
|
||||
// 创建成功后分配角色(如果选择了)
|
||||
if (createRes.code === 0) {
|
||||
@@ -1124,14 +1133,18 @@
|
||||
phone: formData.phone
|
||||
}
|
||||
|
||||
await AccountService.updateAccount(Number(formData.id), data)
|
||||
const updateRes = await AccountService.updateAccount(Number(formData.id), data)
|
||||
if (updateRes.code !== 0) {
|
||||
ElMessage.error(updateRes.msg || updateRes.message || '账号修改失败')
|
||||
return
|
||||
}
|
||||
ElMessage.success('更新成功')
|
||||
}
|
||||
|
||||
dialogVisible.value = false
|
||||
await getAccountList()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} catch {
|
||||
// HTTP 层已展示后端原因,不输出可能包含密码的请求错误对象。
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -154,7 +154,8 @@
|
||||
<ElInput
|
||||
v-model="form.password"
|
||||
type="password"
|
||||
placeholder="请输入登录密码"
|
||||
:placeholder="NEW_LOGIN_PASSWORD_HINT"
|
||||
maxlength="12"
|
||||
show-password
|
||||
/>
|
||||
</ElFormItem>
|
||||
@@ -178,7 +179,8 @@
|
||||
<ElInput
|
||||
v-model="passwordForm.password"
|
||||
type="password"
|
||||
placeholder="请输入新密码(6-20位)"
|
||||
:placeholder="NEW_LOGIN_PASSWORD_HINT"
|
||||
maxlength="12"
|
||||
show-password
|
||||
/>
|
||||
</ElFormItem>
|
||||
@@ -202,6 +204,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
createNewLoginPasswordRule,
|
||||
NEW_LOGIN_PASSWORD_HINT
|
||||
} from '@/utils/validation/loginPassword'
|
||||
import { h, onActivated } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
@@ -388,14 +394,14 @@
|
||||
login_phone: [{ required: true, message: '请输入登录手机号', trigger: 'blur' }],
|
||||
password: [
|
||||
{ required: true, message: '请输入登录密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '密码长度为6-20位', trigger: 'blur' }
|
||||
createNewLoginPasswordRule()
|
||||
]
|
||||
})
|
||||
|
||||
const passwordRules = reactive<FormRules>({
|
||||
password: [
|
||||
{ required: true, message: '请输入新密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '密码长度为6-20位', trigger: 'blur' }
|
||||
createNewLoginPasswordRule()
|
||||
]
|
||||
})
|
||||
|
||||
@@ -799,7 +805,11 @@
|
||||
if (dialogType.value === 'add') {
|
||||
data.login_phone = form.login_phone
|
||||
data.password = form.password
|
||||
await EnterpriseService.createEnterprise(data)
|
||||
const res = await EnterpriseService.createEnterprise(data)
|
||||
if (res.code !== 0) {
|
||||
ElMessage.error(res.msg || res.message || '新增企业失败')
|
||||
return
|
||||
}
|
||||
ElMessage.success('新增成功')
|
||||
} else {
|
||||
await EnterpriseService.updateEnterprise(form.id, data)
|
||||
@@ -809,8 +819,8 @@
|
||||
dialogVisible.value = false
|
||||
formEl.resetFields()
|
||||
await getTableData()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} catch {
|
||||
// HTTP 层统一提示,不记录带密码的请求错误对象。
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
@@ -826,14 +836,18 @@
|
||||
if (valid) {
|
||||
passwordSubmitLoading.value = true
|
||||
try {
|
||||
await EnterpriseService.updateEnterprisePassword(currentEnterpriseId.value, {
|
||||
const res = await EnterpriseService.updateEnterprisePassword(currentEnterpriseId.value, {
|
||||
password: passwordForm.password
|
||||
})
|
||||
if (res.code !== 0) {
|
||||
ElMessage.error(res.msg || res.message || '密码修改失败')
|
||||
return
|
||||
}
|
||||
ElMessage.success('密码修改成功')
|
||||
passwordDialogVisible.value = false
|
||||
formEl.resetFields()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} catch {
|
||||
// HTTP 层已提示,不输出密码。
|
||||
} finally {
|
||||
passwordSubmitLoading.value = false
|
||||
}
|
||||
|
||||
@@ -442,6 +442,12 @@
|
||||
import { AUDIT_PERMISSIONS } from '@/config/constants/audit'
|
||||
import { resolveAuditResourceTarget } from '@/utils/business/auditNavigation'
|
||||
import { openAuditInvestigation } from '@/components/business/audit/investigationController'
|
||||
import { AUGUST_PERMISSIONS } from '@/config/constants/augustIteration'
|
||||
import { normalizeApiError } from '@/utils/business/apiError'
|
||||
import {
|
||||
getExchangePhoneConflictMessage,
|
||||
getExchangeBusinessErrorMessage
|
||||
} from '@/utils/business/exchangeError'
|
||||
|
||||
defineOptions({ name: 'ExchangeManagement' })
|
||||
|
||||
@@ -449,6 +455,38 @@
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
|
||||
const handleExchangeFailure = async (failure: unknown, fallback: string) => {
|
||||
const conflictMessage = getExchangePhoneConflictMessage(failure)
|
||||
if (!conflictMessage) {
|
||||
const normalized = normalizeApiError(failure)
|
||||
if (normalized.kind === 'auth') return // 登录失效由统一 HTTP 层反馈。
|
||||
ElMessage.error(
|
||||
getExchangeBusinessErrorMessage(failure) ||
|
||||
(normalized.kind === 'unknown' ? fallback : normalized.message)
|
||||
)
|
||||
return
|
||||
}
|
||||
const canManageAssociations = hasAuth(AUGUST_PERMISSIONS.phoneAssetAssociation.list)
|
||||
try {
|
||||
await ElMessageBox.confirm(
|
||||
canManageAssociations
|
||||
? conflictMessage
|
||||
: `${conflictMessage}。您暂无管理权限,请联系管理员解除。`,
|
||||
'新资产手机号绑定冲突',
|
||||
{
|
||||
type: 'warning',
|
||||
confirmButtonText: canManageAssociations ? '前往手机号资产关联' : '知道了',
|
||||
cancelButtonText: '暂不处理',
|
||||
showCancelButton: canManageAssociations,
|
||||
dangerouslyUseHTMLString: false
|
||||
}
|
||||
)
|
||||
if (canManageAssociations) await router.push(RoutesAlias.PhoneAssetAssociation)
|
||||
} catch {
|
||||
// 用户关闭提示,不执行解绑或改写换货状态。
|
||||
}
|
||||
}
|
||||
|
||||
const currentExchangeRow = ref<ExchangeResponse | null>(null)
|
||||
const loading = ref(false)
|
||||
const shipDialogVisible = ref(false)
|
||||
@@ -871,7 +909,7 @@
|
||||
label: '换货单号',
|
||||
width: 230,
|
||||
showOverflowTooltip: true,
|
||||
fixed:true,
|
||||
fixed: true,
|
||||
formatter: (row: any) =>
|
||||
h(
|
||||
ElButton,
|
||||
@@ -1535,7 +1573,9 @@
|
||||
requestData.migrate_data = createForm.migrate_data
|
||||
}
|
||||
|
||||
const res = await ExchangeService.createExchange(requestData)
|
||||
const res = await ExchangeService.createExchange(requestData, {
|
||||
requestOptions: { errorMessageMode: 'none' }
|
||||
})
|
||||
if (res.code === 0) {
|
||||
ElMessage.success('换货单创建成功')
|
||||
createDialogVisible.value = false
|
||||
@@ -1543,9 +1583,9 @@
|
||||
return
|
||||
}
|
||||
|
||||
ElMessage.error(res.msg || '换货单创建失败')
|
||||
await handleExchangeFailure(res, '换货单创建失败')
|
||||
} catch (error) {
|
||||
console.error('创建换货单失败:', error)
|
||||
await handleExchangeFailure(error, '换货单创建失败')
|
||||
} finally {
|
||||
createLoading.value = false
|
||||
}
|
||||
@@ -1767,48 +1807,36 @@
|
||||
}
|
||||
|
||||
// 确认完成
|
||||
const handleCompleteExchange = (row: ExchangeResponse) => {
|
||||
const handleCompleteExchange = async (row: ExchangeResponse) => {
|
||||
if (completeLoadingId.value === row.id) return
|
||||
|
||||
completeLoadingId.value = row.id
|
||||
|
||||
ElMessageBox.confirm('确定要确认换货完成吗?', '确认操作', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
beforeClose: async (action, instance, done) => {
|
||||
if (action !== 'confirm') {
|
||||
done()
|
||||
return
|
||||
}
|
||||
|
||||
instance.confirmButtonLoading = true
|
||||
instance.confirmButtonText = '提交中...'
|
||||
|
||||
try {
|
||||
const res = await ExchangeService.completeExchange(row.id)
|
||||
if (res.code === 0) {
|
||||
await refreshExchangeDetail(row.id)
|
||||
await loadExchangeList()
|
||||
ElMessage.success('操作成功')
|
||||
done()
|
||||
return
|
||||
}
|
||||
|
||||
ElMessage.error(res.msg || '确认完成失败')
|
||||
} catch (error) {
|
||||
console.error('确认完成失败:', error)
|
||||
} finally {
|
||||
instance.confirmButtonLoading = false
|
||||
instance.confirmButtonText = '确定'
|
||||
}
|
||||
}
|
||||
})
|
||||
.then(() => {})
|
||||
.finally(() => {
|
||||
completeLoadingId.value = null
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要确认换货完成吗?', '确认操作', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
})
|
||||
.catch(() => {})
|
||||
} catch {
|
||||
completeLoadingId.value = null
|
||||
return
|
||||
}
|
||||
try {
|
||||
const res = await ExchangeService.completeExchange(row.id, {
|
||||
requestOptions: { errorMessageMode: 'none' }
|
||||
})
|
||||
// 刷新失败也不能吞掉原操作的 1202 业务响应。
|
||||
await Promise.allSettled([refreshExchangeDetail(row.id), loadExchangeList()])
|
||||
if (res.code === 0) ElMessage.success('操作成功')
|
||||
else await handleExchangeFailure(res, '确认完成失败')
|
||||
} catch (error) {
|
||||
// 仅重新读取原单状态,不乐观标记完成,错误对象可能包含完整手机号,不输出日志。
|
||||
await Promise.allSettled([refreshExchangeDetail(row.id), loadExchangeList()])
|
||||
await handleExchangeFailure(error, '确认完成失败')
|
||||
} finally {
|
||||
completeLoadingId.value = null
|
||||
}
|
||||
}
|
||||
|
||||
// 旧资产转新
|
||||
|
||||
@@ -62,11 +62,11 @@
|
||||
<p>2. 列格式请设置为文本格式,避免长数字被转为科学计数法。</p>
|
||||
<p>3. <strong>列顺序固定,不可调整。</strong>系统按位置读取,不识别列名。</p>
|
||||
<p style="color: var(--el-color-primary)">
|
||||
4. 必填列:设备标识(第 1 列,支持 VirtualNo、IMEI 或 SN)。
|
||||
4. 必填列:设备标识(第 1 列,支持 VirtualNo、IMEI 或 SN)和独立的 IMEI
|
||||
列。即使设备标识填写 IMEI,IMEI 列也不能留空或仅填空格。
|
||||
</p>
|
||||
<p>
|
||||
5. 可选列:SN、设备名称、设备型号、设备类型、IMEI、制造商、最大 SIM 槽数、卡 1~卡 4
|
||||
ICCID。
|
||||
5. 可选列:SN、设备名称、设备型号、设备类型、制造商、最大 SIM 槽数、卡 1~卡 4 ICCID。
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -162,7 +162,6 @@
|
||||
} from '@/types/api/device'
|
||||
import { RoutesAlias } from '@/router/routesAlias'
|
||||
import { generatePackageCode } from '@/utils/codeGenerator'
|
||||
import { normalizeApiError } from '@/utils/business/apiError'
|
||||
|
||||
defineOptions({ name: 'DeviceTask' })
|
||||
|
||||
|
||||
@@ -146,6 +146,14 @@
|
||||
</ElTabPane>
|
||||
<!-- 提现资料资格 -->
|
||||
<ElTabPane label="提现资料" name="qualification">
|
||||
<ArtSearchBar
|
||||
:filter="qualificationSearchForm"
|
||||
@update:filter="Object.assign(qualificationSearchForm, $event)"
|
||||
:items="qualificationSearchItems"
|
||||
:show-expand="false"
|
||||
@search="handleQualificationSearch"
|
||||
@reset="handleQualificationReset"
|
||||
/>
|
||||
<ArtTableHeader
|
||||
:columnList="qualificationColumnOptions"
|
||||
v-model:columns="qualificationColumnChecks"
|
||||
@@ -172,7 +180,7 @@
|
||||
:pageSize="qualificationPagination.pageSize"
|
||||
:total="qualificationPagination.total"
|
||||
:actions="getQualificationActions"
|
||||
:actionsWidth="100"
|
||||
:actionsWidth="160"
|
||||
:marginTop="10"
|
||||
:height="500"
|
||||
@size-change="handleQualificationSizeChange"
|
||||
@@ -424,7 +432,20 @@
|
||||
<ElDescriptionsItem label="提现单号">{{
|
||||
withdrawalDetail.withdrawal_no
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态">{{ withdrawalDetail.status_name }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="状态"
|
||||
><ElTag
|
||||
:type="
|
||||
WithdrawalStatusMap[withdrawalDetail.status as keyof typeof WithdrawalStatusMap]
|
||||
?.type || 'info'
|
||||
"
|
||||
>{{
|
||||
withdrawalDetail.status_name ||
|
||||
WithdrawalStatusMap[withdrawalDetail.status as keyof typeof WithdrawalStatusMap]
|
||||
?.label ||
|
||||
'-'
|
||||
}}</ElTag
|
||||
></ElDescriptionsItem
|
||||
>
|
||||
<ElDescriptionsItem label="提现金额">{{
|
||||
formatMoney(withdrawalDetail.amount)
|
||||
}}</ElDescriptionsItem>
|
||||
@@ -577,6 +598,9 @@
|
||||
import type { Order, OrderQueryParams } from '@/types/api/order'
|
||||
import {
|
||||
WithdrawalStatusMap,
|
||||
canResubmitWithdrawal,
|
||||
WITHDRAWAL_QUALIFICATION_APPROVAL_FAILED,
|
||||
WITHDRAWAL_QUALIFICATION_STATUS_OPTIONS,
|
||||
WithdrawalMethodMap,
|
||||
COMMISSION_RECORD_SOURCE_MAP,
|
||||
COMMISSION_RECORD_STATUS_OPTIONS,
|
||||
@@ -588,11 +612,13 @@
|
||||
import type { SearchFormItem } from '@/types'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { formatDateTime, formatMoney, yuanToFen } from '@/utils/business/format'
|
||||
|
||||
defineOptions({ name: 'MyCommission' })
|
||||
|
||||
const userStore = useUserStore()
|
||||
const { hasAuth } = useAuth()
|
||||
|
||||
const isSuperAdmin = computed(() => userStore.isSuperAdmin)
|
||||
|
||||
@@ -1021,7 +1047,8 @@
|
||||
{ label: '待审核', value: 1 },
|
||||
{ label: '已通过', value: 2 },
|
||||
{ label: '已拒绝', value: 3 },
|
||||
{ label: '已到账', value: 4 }
|
||||
{ label: '已到账', value: 4 },
|
||||
{ label: '审批提交失败', value: WithdrawalStatus.APPROVAL_SUBMISSION_FAILED }
|
||||
],
|
||||
config: {
|
||||
clearable: true,
|
||||
@@ -1186,6 +1213,7 @@
|
||||
// 刷新表格
|
||||
const handleWithdrawalRefresh = () => {
|
||||
getWithdrawalList()
|
||||
loadSummary()
|
||||
}
|
||||
|
||||
// 处理表格分页变化
|
||||
@@ -1429,6 +1457,24 @@
|
||||
const qualificationLoading = ref(false)
|
||||
const qualificationTableRef = ref()
|
||||
const qualificationList = ref<WithdrawalQualificationItem[]>([])
|
||||
const qualificationSearchForm = reactive({ status: undefined as number | undefined })
|
||||
const qualificationSearchItems: SearchFormItem[] = [
|
||||
{
|
||||
label: '资料状态',
|
||||
prop: 'status',
|
||||
type: 'select',
|
||||
options: WITHDRAWAL_QUALIFICATION_STATUS_OPTIONS,
|
||||
config: { clearable: true, placeholder: '全部' }
|
||||
}
|
||||
]
|
||||
const handleQualificationSearch = () => {
|
||||
qualificationPagination.page = 1
|
||||
void getQualificationList()
|
||||
}
|
||||
const handleQualificationReset = () => {
|
||||
qualificationSearchForm.status = undefined
|
||||
handleQualificationSearch()
|
||||
}
|
||||
const qualificationPagination = reactive({
|
||||
page: 1,
|
||||
pageSize: 10,
|
||||
@@ -1469,7 +1515,15 @@
|
||||
width: 120,
|
||||
formatter: (row: WithdrawalQualificationItem) => {
|
||||
const tagType = row.status === 0 ? 'warning' : row.status === 1 ? 'success' : 'danger'
|
||||
return h(ElTag, { type: tagType }, () => row.status_name || String(row.status))
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: tagType },
|
||||
() =>
|
||||
row.status_name ||
|
||||
WITHDRAWAL_QUALIFICATION_STATUS_OPTIONS.find((item) => item.value === row.status)
|
||||
?.label ||
|
||||
String(row.status)
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1512,7 +1566,8 @@
|
||||
try {
|
||||
const res = await CommissionService.getWithdrawalQualifications(currentShopId.value, {
|
||||
page: qualificationPagination.page,
|
||||
page_size: qualificationPagination.pageSize
|
||||
page_size: qualificationPagination.pageSize,
|
||||
status: qualificationSearchForm.status
|
||||
})
|
||||
if (res.code === 0) {
|
||||
qualificationList.value = res.data.items || []
|
||||
@@ -1632,6 +1687,17 @@
|
||||
}
|
||||
|
||||
const getQualificationActions = (row: WithdrawalQualificationItem) => {
|
||||
if (row.status === WITHDRAWAL_QUALIFICATION_APPROVAL_FAILED) {
|
||||
return hasAuth('my_commission:add')
|
||||
? [
|
||||
{
|
||||
label: '重新提交资料',
|
||||
handler: () => showQualificationDialog(),
|
||||
type: 'primary' as const
|
||||
}
|
||||
]
|
||||
: []
|
||||
}
|
||||
if (!userStore.isSuperAdmin) return []
|
||||
return [
|
||||
{
|
||||
@@ -1699,7 +1765,7 @@
|
||||
type: 'primary'
|
||||
}
|
||||
]
|
||||
if (row.status === WithdrawalStatus.REJECTED) {
|
||||
if (canResubmitWithdrawal(row.status) && hasAuth('my_commission:add')) {
|
||||
actions.push({
|
||||
label: '重新提交',
|
||||
handler: () => openResubmitDialog(row),
|
||||
@@ -1781,6 +1847,7 @@
|
||||
})
|
||||
|
||||
const openResubmitDialog = (row: WithdrawalRequestItem) => {
|
||||
if (!canResubmitWithdrawal(row.status) || !hasAuth('my_commission:add')) return
|
||||
resubmitTargetId.value = row.id
|
||||
resubmitForm.amount = Number((row.amount / 100).toFixed(2))
|
||||
resubmitForm.withdrawal_method = row.withdrawal_method || WithdrawalMethod.ALIPAY
|
||||
@@ -1812,11 +1879,15 @@
|
||||
if (resubmitForm.invoice_keys.length > 0) {
|
||||
params.invoice_keys = resubmitForm.invoice_keys
|
||||
}
|
||||
await CommissionService.resubmitWithdrawalRequest(
|
||||
const res = await CommissionService.resubmitWithdrawalRequest(
|
||||
currentShopId.value!,
|
||||
resubmitTargetId.value!,
|
||||
params
|
||||
)
|
||||
if (res.code !== 0) {
|
||||
ElMessage.error(res.msg || res.message || '重新提交失败')
|
||||
return
|
||||
}
|
||||
ElMessage.success('重新提交成功')
|
||||
resubmitDialogVisible.value = false
|
||||
await getWithdrawalList()
|
||||
|
||||
@@ -75,6 +75,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { WithdrawalStatus as WithdrawalStatusValue } from '@/types/api/commission'
|
||||
import { h } from 'vue'
|
||||
import { CommissionService, ShopService } from '@/api/modules'
|
||||
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
|
||||
@@ -121,7 +122,8 @@
|
||||
{ label: '待审核', value: 1 },
|
||||
{ label: '已通过', value: 2 },
|
||||
{ label: '已拒绝', value: 3 },
|
||||
{ label: '已到账', value: 4 }
|
||||
{ label: '已到账', value: 4 },
|
||||
{ label: '审批提交失败', value: WithdrawalStatusValue.APPROVAL_SUBMISSION_FAILED }
|
||||
]
|
||||
|
||||
// 搜索表单配置
|
||||
|
||||
@@ -69,7 +69,8 @@
|
||||
<ElInput
|
||||
v-model="accountForm.password"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
:placeholder="NEW_LOGIN_PASSWORD_HINT"
|
||||
maxlength="12"
|
||||
show-password
|
||||
/>
|
||||
</ElFormItem>
|
||||
@@ -225,6 +226,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
createNewLoginPasswordRule,
|
||||
NEW_LOGIN_PASSWORD_HINT
|
||||
} from '@/utils/validation/loginPassword'
|
||||
import { h, computed, ref, reactive, onMounted, nextTick } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
@@ -584,7 +589,7 @@
|
||||
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
|
||||
password: [
|
||||
{ required: true, message: '请输入密码', trigger: 'blur' },
|
||||
{ min: 6, max: 20, message: '密码长度为 6-20 个字符', trigger: 'blur' }
|
||||
createNewLoginPasswordRule()
|
||||
],
|
||||
phone: [
|
||||
{ required: true, message: '请输入手机号', trigger: 'blur' },
|
||||
@@ -631,15 +636,18 @@
|
||||
data.enterprise_id = entityId
|
||||
}
|
||||
|
||||
await AccountService.createAccount(data)
|
||||
const res = await AccountService.createAccount(data)
|
||||
if (res.code !== 0) {
|
||||
ElMessage.error(res.msg || res.message || '添加账号失败')
|
||||
return
|
||||
}
|
||||
ElMessage.success('添加账号成功')
|
||||
dialogVisible.value = false
|
||||
|
||||
// 刷新列表
|
||||
await getTableData()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
console.log('添加账号失败')
|
||||
} catch {
|
||||
// HTTP 层已提示,不输出包含新密码的请求错误对象。
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ interface BuildAgentRechargeActionsOptions {
|
||||
onConfirmPayment: (row: AgentRecharge) => void
|
||||
onReject: (row: AgentRecharge) => void
|
||||
onTriggerApproval: (row: AgentRecharge) => void
|
||||
canCreateOffline?: boolean
|
||||
onRestart?: (row: AgentRecharge) => void
|
||||
}
|
||||
|
||||
export const buildAgentRechargeActions = (
|
||||
@@ -31,7 +33,8 @@ export const buildAgentRechargeActions = (
|
||||
![
|
||||
AgentRechargeStatus.COMPLETED,
|
||||
AgentRechargeStatus.REJECTED,
|
||||
AgentRechargeStatus.CLOSED
|
||||
AgentRechargeStatus.CLOSED,
|
||||
AgentRechargeStatus.APPROVAL_SUBMISSION_FAILED
|
||||
].includes(row.status)
|
||||
|
||||
if (
|
||||
@@ -67,6 +70,16 @@ export const buildAgentRechargeActions = (
|
||||
})
|
||||
}
|
||||
|
||||
if (
|
||||
row.status === AgentRechargeStatus.APPROVAL_SUBMISSION_FAILED &&
|
||||
row.payment_method === 'offline' &&
|
||||
options.canCreateOffline &&
|
||||
options.onRestart &&
|
||||
options.hasAuth('agent_recharge:create')
|
||||
) {
|
||||
actions.push({ label: '重新发起', handler: () => options.onRestart?.(row), type: 'primary' })
|
||||
}
|
||||
|
||||
if (
|
||||
!hasApprovalRecord &&
|
||||
row.status === AgentRechargeStatus.PENDING &&
|
||||
|
||||
@@ -72,7 +72,8 @@
|
||||
3: 'success', // 已完成
|
||||
4: 'info', // 已关闭
|
||||
5: 'danger', // 已退款
|
||||
6: 'danger' // 已驳回
|
||||
6: 'danger', // 已驳回
|
||||
7: 'danger' // 审批提交失败
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
}
|
||||
@@ -131,7 +132,11 @@
|
||||
{
|
||||
label: '状态',
|
||||
render: (data) =>
|
||||
h(ElTag, { type: getStatusType(data.status) }, () => data.status_name || '-')
|
||||
h(
|
||||
ElTag,
|
||||
{ type: getStatusType(data.status) },
|
||||
() => data.status_name || (data.status === 7 ? '审批提交失败' : '-')
|
||||
)
|
||||
},
|
||||
...(!isRestrictedCustomerRole.value
|
||||
? [
|
||||
|
||||
@@ -359,6 +359,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { AgentRechargeStatus as AgentRechargeStatusValue } from '@/types/api/agentRecharge'
|
||||
import { h, watch } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import QrcodeVue from 'qrcode.vue'
|
||||
@@ -568,7 +569,8 @@
|
||||
{ label: '已完成', value: 3 },
|
||||
{ label: '已关闭', value: 4 },
|
||||
{ label: '已退款', value: 5 },
|
||||
{ label: '已驳回', value: 6 }
|
||||
{ label: '已驳回', value: 6 },
|
||||
{ label: '审批提交失败', value: AgentRechargeStatusValue.APPROVAL_SUBMISSION_FAILED }
|
||||
],
|
||||
config: { clearable: true }
|
||||
},
|
||||
@@ -781,7 +783,8 @@
|
||||
3: 'success', // 已完成
|
||||
4: 'info', // 已关闭
|
||||
5: 'danger', // 已退款
|
||||
6: 'danger' // 已驳回
|
||||
6: 'danger', // 已驳回
|
||||
7: 'danger' // 审批提交失败
|
||||
}
|
||||
return statusMap[status] || 'info'
|
||||
}
|
||||
@@ -848,7 +851,15 @@
|
||||
label: '状态',
|
||||
width: 100,
|
||||
formatter: (row: AgentRecharge) => {
|
||||
return h(ElTag, { type: getStatusType(row.status) }, () => row.status_name || '-')
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: getStatusType(row.status) },
|
||||
() =>
|
||||
row.status_name ||
|
||||
(row.status === AgentRechargeStatusValue.APPROVAL_SUBMISSION_FAILED
|
||||
? '审批提交失败'
|
||||
: '-')
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -1586,7 +1597,12 @@
|
||||
onViewPaymentVoucher: handleViewPaymentVoucher,
|
||||
onConfirmPayment: handleShowConfirmPay,
|
||||
onReject: handleShowReject,
|
||||
onTriggerApproval: handleTriggerApproval
|
||||
onTriggerApproval: handleTriggerApproval,
|
||||
canCreateOffline: isPlatformAccount.value,
|
||||
onRestart: async () => {
|
||||
if (!isPlatformAccount.value || !hasAuth('agent_recharge:create')) return
|
||||
await showCreateDialog()
|
||||
}
|
||||
})
|
||||
if (isPlatformAccount.value && hasAuth(AUDIT_PERMISSIONS.agentRechargeEntry)) {
|
||||
const resourceTarget = resolveAuditResourceTarget({
|
||||
|
||||
@@ -11,7 +11,17 @@
|
||||
<h2 class="detail-title">核销申请详情</h2>
|
||||
<ElButton
|
||||
v-if="
|
||||
application?.recoverable &&
|
||||
application &&
|
||||
canResubmitApplication(application) &&
|
||||
hasAuth(AUGUST_PERMISSIONS.employeeCollection.applicationUpdate)
|
||||
"
|
||||
type="primary"
|
||||
@click="applicationDialogVisible = true"
|
||||
>修改并重新提交</ElButton
|
||||
>
|
||||
<ElButton
|
||||
v-if="
|
||||
canRecoverApplicationApproval(application) &&
|
||||
hasAuth(AUGUST_PERMISSIONS.employeeCollection.applicationRecoverApproval)
|
||||
"
|
||||
type="warning"
|
||||
@@ -23,6 +33,11 @@
|
||||
</div>
|
||||
|
||||
<DetailPage v-if="application" :sections="detailSections" :data="application" />
|
||||
<ApplicationFormDialog
|
||||
v-model="applicationDialogVisible"
|
||||
:application="application"
|
||||
@success="loadDetail"
|
||||
/>
|
||||
|
||||
<div v-if="loading" class="loading-container">
|
||||
<ElIcon class="is-loading"><Loading /></ElIcon>
|
||||
@@ -116,7 +131,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, h, onMounted, ref } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import {
|
||||
ElButton,
|
||||
@@ -124,6 +139,7 @@
|
||||
ElIcon,
|
||||
ElMessage,
|
||||
ElMessageBox,
|
||||
ElTag,
|
||||
ElTable,
|
||||
ElTableColumn,
|
||||
ElTimeline,
|
||||
@@ -145,9 +161,14 @@
|
||||
import { getErrorMessage, toVoucherKeyList } from '@/utils/business'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import PaymentVoucherDialog from '@/components/business/PaymentVoucherDialog.vue'
|
||||
import ApplicationFormDialog from './components/ApplicationFormDialog.vue'
|
||||
import {
|
||||
formatCollectionCurrency,
|
||||
canRecoverApplicationApproval,
|
||||
canResubmitApplication,
|
||||
getApplicationSubmissionLabel,
|
||||
getApplicationStatusLabel,
|
||||
getApplicationStatusTagType,
|
||||
getBillStatusLabel
|
||||
} from '../employeeCollectionDisplay'
|
||||
|
||||
@@ -159,6 +180,7 @@
|
||||
|
||||
const loading = ref(false)
|
||||
const recovering = ref(false)
|
||||
const applicationDialogVisible = ref(false)
|
||||
const detail = ref<EmployeeCollectionApplicationDetailData | null>(null)
|
||||
|
||||
const application = computed<EmployeeCollectionApplication | null>(
|
||||
@@ -196,12 +218,16 @@
|
||||
},
|
||||
{
|
||||
label: '状态',
|
||||
prop: 'status',
|
||||
formatter: (value, data) => data.status_name || getApplicationStatusLabel(value)
|
||||
render: (data: EmployeeCollectionApplication) =>
|
||||
h(
|
||||
ElTag,
|
||||
{ type: getApplicationStatusTagType(data.status) },
|
||||
() => data.status_name || getApplicationStatusLabel(data.status)
|
||||
)
|
||||
},
|
||||
{
|
||||
label: '审批提交状态',
|
||||
formatter: (_, data) => data.approval_submission_name || '-'
|
||||
formatter: (_, data) => getApplicationSubmissionLabel(data)
|
||||
},
|
||||
{
|
||||
label: '提交失败摘要',
|
||||
@@ -270,7 +296,8 @@
|
||||
|
||||
const recoverApproval = async () => {
|
||||
if (
|
||||
!application.value?.recoverable ||
|
||||
!application.value ||
|
||||
!canRecoverApplicationApproval(application.value) ||
|
||||
!hasAuth(AUGUST_PERMISSIONS.employeeCollection.applicationRecoverApproval) ||
|
||||
recovering.value
|
||||
) {
|
||||
|
||||
@@ -76,6 +76,8 @@
|
||||
import {
|
||||
APPLICATION_STATUS_OPTIONS,
|
||||
canResubmitApplication,
|
||||
canRecoverApplicationApproval,
|
||||
getApplicationSubmissionLabel,
|
||||
formatCollectionCurrency,
|
||||
getApplicationStatusLabel,
|
||||
getApplicationStatusTagType,
|
||||
@@ -191,15 +193,15 @@
|
||||
prop: 'approval_submission_name',
|
||||
label: '提交状态',
|
||||
width: 130,
|
||||
formatter: (row: EmployeeCollectionApplication) => row.approval_submission_name || '-'
|
||||
formatter: getApplicationSubmissionLabel
|
||||
},
|
||||
{
|
||||
prop: 'recoverable',
|
||||
label: '可恢复',
|
||||
width: 90,
|
||||
formatter: (row: EmployeeCollectionApplication) =>
|
||||
h(ElTag, { type: row.recoverable ? 'warning' : 'info' }, () =>
|
||||
row.recoverable ? '是' : '否'
|
||||
h(ElTag, { type: canRecoverApplicationApproval(row) ? 'warning' : 'info' }, () =>
|
||||
canRecoverApplicationApproval(row) ? '是' : '否'
|
||||
)
|
||||
},
|
||||
{
|
||||
@@ -291,7 +293,7 @@
|
||||
|
||||
const handleRecoverApproval = async (row: EmployeeCollectionApplication) => {
|
||||
if (
|
||||
!row.recoverable ||
|
||||
!canRecoverApplicationApproval(row) ||
|
||||
!hasAuth(AUGUST_PERMISSIONS.employeeCollection.applicationRecoverApproval)
|
||||
) {
|
||||
return
|
||||
@@ -322,7 +324,7 @@
|
||||
actions.push({ label: '修改并重新提交', handler: () => handleResubmit(row), type: 'primary' })
|
||||
}
|
||||
if (
|
||||
row.recoverable &&
|
||||
canRecoverApplicationApproval(row) &&
|
||||
hasAuth(AUGUST_PERMISSIONS.employeeCollection.applicationRecoverApproval)
|
||||
) {
|
||||
actions.push({
|
||||
|
||||
@@ -24,7 +24,8 @@ export const APPLICATION_STATUS = {
|
||||
PENDING: 0,
|
||||
APPROVED: 1,
|
||||
REJECTED: 2,
|
||||
REVOKED: 3
|
||||
REVOKED: 3,
|
||||
APPROVAL_SUBMISSION_FAILED: 4
|
||||
} as const
|
||||
|
||||
/**
|
||||
@@ -77,7 +78,8 @@ export const APPLICATION_STATUS_OPTIONS = [
|
||||
{ label: '审批中', value: APPLICATION_STATUS.PENDING },
|
||||
{ label: '已通过', value: APPLICATION_STATUS.APPROVED },
|
||||
{ label: '已驳回', value: APPLICATION_STATUS.REJECTED },
|
||||
{ label: '已关闭', value: APPLICATION_STATUS.REVOKED }
|
||||
{ label: '已关闭', value: APPLICATION_STATUS.REVOKED },
|
||||
{ label: '审批提交失败', value: APPLICATION_STATUS.APPROVAL_SUBMISSION_FAILED }
|
||||
]
|
||||
|
||||
/**
|
||||
@@ -154,6 +156,8 @@ export const getApplicationStatusTagType = (
|
||||
status?: EmployeeCollectionApplicationStatus | null
|
||||
): TagType => {
|
||||
switch (status) {
|
||||
case APPLICATION_STATUS.APPROVAL_SUBMISSION_FAILED:
|
||||
return 'danger'
|
||||
case APPLICATION_STATUS.PENDING:
|
||||
return 'warning'
|
||||
case APPLICATION_STATUS.APPROVED:
|
||||
@@ -211,5 +215,32 @@ export const canCreateApplication = (bill: EmployeeCollectionBill): boolean => {
|
||||
* 申请是否可修改并重新提交
|
||||
*/
|
||||
export const canResubmitApplication = (application: EmployeeCollectionApplication): boolean => {
|
||||
return application.status === APPLICATION_STATUS.REJECTED
|
||||
return (
|
||||
application.status === APPLICATION_STATUS.REJECTED ||
|
||||
application.status === APPLICATION_STATUS.APPROVAL_SUBMISSION_FAILED
|
||||
)
|
||||
}
|
||||
|
||||
export const canRecoverApplicationApproval = (
|
||||
application?: EmployeeCollectionApplication | null
|
||||
): boolean =>
|
||||
!!application &&
|
||||
application.status !== APPLICATION_STATUS.APPROVAL_SUBMISSION_FAILED &&
|
||||
application.recoverable === true
|
||||
|
||||
export const getApplicationSubmissionLabel = (
|
||||
application: EmployeeCollectionApplication
|
||||
): string => {
|
||||
const labels: Record<number, string> = {
|
||||
0: '待提交',
|
||||
1: '提交中',
|
||||
2: '已提交',
|
||||
3: '提交失败',
|
||||
4: '结果未知'
|
||||
}
|
||||
const status = application.approval_submission_status
|
||||
return (
|
||||
application.approval_submission_name ||
|
||||
(status == null ? '-' : (labels[status] ?? String(status)))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed, h, reactive } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElButton, ElCard, ElIcon, ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { ElButton, ElCard, ElIcon, ElMessage, ElMessageBox, ElTag } from 'element-plus'
|
||||
import type { FormRules } from 'element-plus'
|
||||
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
|
||||
import DetailPage from '@/components/common/DetailPage.vue'
|
||||
@@ -201,7 +201,10 @@
|
||||
import {
|
||||
RefundMethodOptions,
|
||||
canManualApproveRefund,
|
||||
canResubmitRefund
|
||||
canResubmitRefund,
|
||||
canRecoverRefundApproval,
|
||||
RefundStatusNameMap,
|
||||
RefundStatusTypeMap
|
||||
} from '@/config/constants/refund'
|
||||
import { fenToYuan, formatDateTime, yuanToFen } from '@/utils/business/format'
|
||||
import { toVoucherKeyList, getErrorMessage } from '@/utils/business'
|
||||
@@ -431,7 +434,15 @@
|
||||
{ label: '订单号', prop: 'order_no' },
|
||||
{ label: '资产标识符', prop: 'asset_identifier' },
|
||||
{ label: '资产类型', prop: 'asset_type' },
|
||||
{ label: '退款状态', prop: 'status_name', formatter: (value) => value || '-' },
|
||||
{
|
||||
label: '退款状态',
|
||||
render: (data: Refund) =>
|
||||
h(
|
||||
ElTag,
|
||||
{ type: RefundStatusTypeMap[data.status] || 'info' },
|
||||
() => data.status_name || RefundStatusNameMap[data.status] || '-'
|
||||
)
|
||||
},
|
||||
...(!isRestrictedCustomerRole.value
|
||||
? [
|
||||
{
|
||||
@@ -735,7 +746,7 @@
|
||||
return canResubmitRefund(item)
|
||||
}
|
||||
|
||||
const canRecoverApproval = (item: Refund) => item.approval_recoverable === true
|
||||
const canRecoverApproval = canRecoverRefundApproval
|
||||
|
||||
const isApprovalRecoveryConflict = (error: unknown, message = '') => {
|
||||
if (!error || typeof error !== 'object') {
|
||||
|
||||
@@ -207,6 +207,8 @@
|
||||
RefundMethodOptions,
|
||||
RefundStatusTypeMap,
|
||||
canResubmitRefund,
|
||||
canRecoverRefundApproval,
|
||||
RefundStatusNameMap,
|
||||
canManualApproveRefund
|
||||
} from '@/config/constants/refund'
|
||||
import type { SearchFormItem } from '@/types'
|
||||
@@ -344,7 +346,8 @@
|
||||
{ label: '已拒绝', value: 3 },
|
||||
{ label: '已退回', value: 4 },
|
||||
{ label: '原路退款处理中', value: 5 },
|
||||
{ label: '原路退款失败', value: 6 }
|
||||
{ label: '原路退款失败', value: 6 },
|
||||
{ label: '审批提交失败', value: RefundStatus.APPROVAL_SUBMISSION_FAILED }
|
||||
],
|
||||
config: {
|
||||
clearable: true
|
||||
@@ -477,7 +480,7 @@
|
||||
prop: 'refund_no',
|
||||
label: '退款单号',
|
||||
minWidth: 210,
|
||||
fixed:true,
|
||||
fixed: true,
|
||||
formatter: (row: Refund) => {
|
||||
return h(
|
||||
'span',
|
||||
@@ -608,7 +611,11 @@
|
||||
label: '状态',
|
||||
width: 100,
|
||||
formatter: (row: Refund) => {
|
||||
return h(ElTag, { type: getStatusType(row.status) }, () => row.status_name || '-')
|
||||
return h(
|
||||
ElTag,
|
||||
{ type: getStatusType(row.status) },
|
||||
() => row.status_name || RefundStatusNameMap[row.status] || '-'
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -953,7 +960,7 @@
|
||||
return row.status === RefundStatus.PENDING && approvalStatusEmpty
|
||||
}
|
||||
|
||||
const canRecoverApproval = (row: Refund) => row.approval_recoverable === true
|
||||
const canRecoverApproval = canRecoverRefundApproval
|
||||
|
||||
const isApprovalRecoveryConflict = (error: unknown, message = '') => {
|
||||
if (!error || typeof error !== 'object') {
|
||||
|
||||
@@ -15,6 +15,52 @@
|
||||
<!-- 详情内容 -->
|
||||
<DetailPage v-if="detailData" :sections="detailSections" :data="detailData" />
|
||||
|
||||
<ElCard v-if="detailData" style="margin-top: 20px" shadow="never">
|
||||
<div class="order-items-section">
|
||||
<h3 class="section-title">支付记录</h3>
|
||||
<ElTable :data="detailData.payments || []" border empty-text="暂无支付记录">
|
||||
<ElTableColumn prop="payment_no" label="支付单号" min-width="200" />
|
||||
<ElTableColumn label="支付方式" width="100">
|
||||
<template #default="{ row }">{{ getPaymentMethodText(row.payment_method) }}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="支付渠道" min-width="140">
|
||||
<template #default="{ row }">{{
|
||||
getOrderPaymentChannelName(row.payment_channel)
|
||||
}}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="收款商户" min-width="180" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.payment_merchant_name || '-' }}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="交易流水号" min-width="200" show-overflow-tooltip>
|
||||
<template #default="{ row }">{{ row.third_party_trade_no || '-' }}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="金额(元)" width="120">
|
||||
<template #default="{ row }">{{ formatCurrency(row.amount) }}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="支付单状态" width="120">
|
||||
<template #default="{ row }">
|
||||
<ElTag :type="getOrderPaymentRecordTagType(row.status)">
|
||||
{{ getOrderPaymentRecordStatusName(row.status, row.status_name) }}
|
||||
</ElTag>
|
||||
</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="创建时间" width="180">
|
||||
<template #default="{ row }">{{ formatDateTime(row.created_at) }}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="支付时间" width="180">
|
||||
<template #default="{ row }">{{
|
||||
row.paid_at ? formatDateTime(row.paid_at) : '-'
|
||||
}}</template>
|
||||
</ElTableColumn>
|
||||
<ElTableColumn label="过期时间" width="180">
|
||||
<template #default="{ row }">{{
|
||||
row.expire_at ? formatDateTime(row.expire_at) : '-'
|
||||
}}</template>
|
||||
</ElTableColumn>
|
||||
</ElTable>
|
||||
</div>
|
||||
</ElCard>
|
||||
|
||||
<!-- 订单列表 -->
|
||||
<div v-if="detailData && detailData.items && detailData.items.length > 0">
|
||||
<ElCard style="margin-top: 20px" shadow="never">
|
||||
@@ -66,13 +112,13 @@
|
||||
<script setup lang="ts">
|
||||
import { h, onMounted, ref, computed } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { ElButton, ElCard, ElIcon, ElMessage, ElTable, ElTableColumn } from 'element-plus'
|
||||
import { ElButton, ElCard, ElIcon, ElMessage, ElTable, ElTableColumn, ElTag } from 'element-plus'
|
||||
import { ArrowLeft, Loading } from '@element-plus/icons-vue'
|
||||
import DetailPage from '@/components/common/DetailPage.vue'
|
||||
import type { DetailField, DetailSection } from '@/components/common/DetailPage.vue'
|
||||
import { OrderService } from '@/api/modules'
|
||||
import type {
|
||||
Order,
|
||||
OrderDetail,
|
||||
OrderCommissionResult,
|
||||
OrderCommissionStatus,
|
||||
OrderOperatorType
|
||||
@@ -81,6 +127,11 @@
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { hasVoucherKeys, toVoucherKeyList } from '@/utils/business'
|
||||
import {
|
||||
getOrderPaymentChannelName,
|
||||
getOrderPaymentRecordStatusName,
|
||||
getOrderPaymentRecordTagType
|
||||
} from '@/utils/business/orderPayment'
|
||||
import PaymentVoucherDialog from '@/components/business/PaymentVoucherDialog.vue'
|
||||
|
||||
defineOptions({ name: 'OrderDetail' })
|
||||
@@ -91,7 +142,7 @@
|
||||
const userStore = useUserStore()
|
||||
|
||||
const loading = ref(false)
|
||||
const detailData = ref<Order | null>(null)
|
||||
const detailData = ref<OrderDetail | null>(null)
|
||||
const paymentVoucherFileKeys = ref<string[]>([])
|
||||
|
||||
// 格式化货币 - 将分转换为元
|
||||
@@ -203,6 +254,9 @@
|
||||
prop: 'payment_method',
|
||||
formatter: (value) => getPaymentMethodText(value)
|
||||
},
|
||||
{ label: '交易流水号', prop: 'third_party_trade_no', formatter: (value) => value || '-' },
|
||||
{ label: '支付渠道', prop: 'payment_channel', formatter: getOrderPaymentChannelName },
|
||||
{ label: '收款商户', prop: 'payment_merchant_name', formatter: (value) => value || '-' },
|
||||
{
|
||||
label: '订单金额',
|
||||
prop: 'total_amount',
|
||||
|
||||
@@ -270,6 +270,7 @@
|
||||
ShopService
|
||||
} from '@/api/modules'
|
||||
import { ElMessage, ElMessageBox, ElTag } from 'element-plus'
|
||||
import { getOrderPaymentChannelName } from '@/utils/business/orderPayment'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import type {
|
||||
Order,
|
||||
@@ -546,6 +547,9 @@
|
||||
|
||||
options.push(
|
||||
{ label: '支付方式', prop: 'payment_method' },
|
||||
{ label: '交易流水号', prop: 'third_party_trade_no' },
|
||||
{ label: '支付渠道', prop: 'payment_channel' },
|
||||
{ label: '收款商户', prop: 'payment_merchant_name' },
|
||||
{ label: '支付时间', prop: 'paid_at' }
|
||||
)
|
||||
|
||||
@@ -879,7 +883,7 @@
|
||||
prop: 'order_no',
|
||||
label: '订单编号',
|
||||
minWidth: 220,
|
||||
fixed:true,
|
||||
fixed: true,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: Order) => {
|
||||
return h(
|
||||
@@ -1072,6 +1076,26 @@
|
||||
}
|
||||
|
||||
baseColumns.push(
|
||||
{
|
||||
prop: 'third_party_trade_no',
|
||||
label: '交易流水号',
|
||||
minWidth: 200,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: Order) => row.third_party_trade_no || '-'
|
||||
},
|
||||
{
|
||||
prop: 'payment_channel',
|
||||
label: '支付渠道',
|
||||
width: 140,
|
||||
formatter: (row: Order) => getOrderPaymentChannelName(row.payment_channel)
|
||||
},
|
||||
{
|
||||
prop: 'payment_merchant_name',
|
||||
label: '收款商户',
|
||||
minWidth: 180,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: Order) => row.payment_merchant_name || '-'
|
||||
},
|
||||
{
|
||||
prop: 'payment_method',
|
||||
label: '支付方式',
|
||||
|
||||
@@ -16,11 +16,7 @@
|
||||
@refresh="handleRefresh"
|
||||
>
|
||||
<template #left>
|
||||
<ElButton
|
||||
v-if="canCreate"
|
||||
type="primary"
|
||||
@click="openFormDialog('create')"
|
||||
>
|
||||
<ElButton v-if="canCreate" type="primary" @click="openFormDialog('create')">
|
||||
新增规则
|
||||
</ElButton>
|
||||
</template>
|
||||
@@ -121,7 +117,6 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, h, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox, ElTag, FormInstance, FormRules } from 'element-plus'
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { PackageManageService, PackageTrafficAlertService } from '@/api/modules'
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
@@ -218,12 +213,6 @@
|
||||
minWidth: 180,
|
||||
showOverflowTooltip: true
|
||||
},
|
||||
{
|
||||
prop: 'real_data_mb',
|
||||
label: '当前真流量额度',
|
||||
width: 150,
|
||||
formatter: (row: PackageTrafficAlertRuleItem) => formatMbToGb(row.real_data_mb)
|
||||
},
|
||||
{
|
||||
prop: 'threshold_percent',
|
||||
label: '预警阈值',
|
||||
@@ -261,7 +250,6 @@
|
||||
|
||||
const columnOptions = [
|
||||
{ label: '套餐名称', prop: 'package_name' },
|
||||
{ label: '当前真流量额度', prop: 'real_data_mb' },
|
||||
{ label: '预警阈值', prop: 'threshold_percent' },
|
||||
{ label: '状态', prop: 'enabled' },
|
||||
{ label: '备注', prop: 'remark' },
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
import type { DetailSection } from '@/components/common/DetailPage.vue'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { normalizeApiError } from '@/utils/business/apiError'
|
||||
import { formatTrafficAlertMb } from '@/utils/business/trafficAlert'
|
||||
import {
|
||||
getTrafficAlertAssetTypeName,
|
||||
getTrafficAlertNotificationStatusOption
|
||||
@@ -78,6 +79,9 @@
|
||||
title: '触发信息',
|
||||
fields: [
|
||||
{ label: '套餐', prop: 'package_name' },
|
||||
{ label: '阈值流量(MB)', prop: 'threshold_mb', formatter: formatTrafficAlertMb },
|
||||
{ label: '已用流量(MB,含加油包)', prop: 'used_mb', formatter: formatTrafficAlertMb },
|
||||
{ label: '总流量(MB,含加油包)', prop: 'limit_mb', formatter: formatTrafficAlertMb },
|
||||
{
|
||||
label: '触发阈值',
|
||||
prop: 'threshold_percent',
|
||||
|
||||
@@ -72,6 +72,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { formatTrafficAlertMb } from '@/utils/business/trafficAlert'
|
||||
import { computed, h, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElButton, ElMessage, ElMessageBox, ElRadio, ElRadioGroup, ElTag } from 'element-plus'
|
||||
@@ -80,7 +81,6 @@
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { useCheckedColumns } from '@/composables/useCheckedColumns'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import { normalizeApiError } from '@/utils/business/apiError'
|
||||
import {
|
||||
AUGUST_PERMISSIONS,
|
||||
TRAFFIC_ALERT_ASSET_TYPE_OPTIONS,
|
||||
@@ -285,10 +285,8 @@
|
||||
formatter: (row: PackageTrafficAlertRecordItem) => {
|
||||
const packageName = row.package_name || '-'
|
||||
if (!canViewDetail.value) return packageName
|
||||
return h(
|
||||
ElButton,
|
||||
{ link: true, type: 'primary', onClick: () => goDetail(row) },
|
||||
() => h('span', { style: { textDecoration: 'underline' } }, packageName)
|
||||
return h(ElButton, { link: true, type: 'primary', onClick: () => goDetail(row) }, () =>
|
||||
h('span', { style: { textDecoration: 'underline' } }, packageName)
|
||||
)
|
||||
}
|
||||
},
|
||||
@@ -343,6 +341,24 @@
|
||||
width: 180,
|
||||
formatter: (row: PackageTrafficAlertRecordItem) => formatDateTime(row.triggered_at)
|
||||
},
|
||||
{
|
||||
prop: 'threshold_mb',
|
||||
label: '阈值流量(MB)',
|
||||
width: 150,
|
||||
formatter: (row: PackageTrafficAlertRecordItem) => formatTrafficAlertMb(row.threshold_mb)
|
||||
},
|
||||
{
|
||||
prop: 'used_mb',
|
||||
label: '已用流量(MB,含加油包)',
|
||||
minWidth: 210,
|
||||
formatter: (row: PackageTrafficAlertRecordItem) => formatTrafficAlertMb(row.used_mb)
|
||||
},
|
||||
{
|
||||
prop: 'limit_mb',
|
||||
label: '总流量(MB,含加油包)',
|
||||
minWidth: 200,
|
||||
formatter: (row: PackageTrafficAlertRecordItem) => formatTrafficAlertMb(row.limit_mb)
|
||||
},
|
||||
{
|
||||
prop: 'notification_status',
|
||||
label: '通知状态',
|
||||
@@ -366,6 +382,9 @@
|
||||
{ label: '资产类型', prop: 'asset_type' },
|
||||
{ label: '资产标识', prop: 'asset_identifier' },
|
||||
{ label: '触发阈值', prop: 'threshold_percent' },
|
||||
{ label: '阈值流量(MB)', prop: 'threshold_mb' },
|
||||
{ label: '已用流量(MB,含加油包)', prop: 'used_mb' },
|
||||
{ label: '总流量(MB,含加油包)', prop: 'limit_mb' },
|
||||
{ label: '触发时间', prop: 'triggered_at' },
|
||||
{ label: '通知状态', prop: 'notification_status' }
|
||||
]
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<h2>代理注册申请详情</h2>
|
||||
<ElButton
|
||||
v-if="
|
||||
application?.recoverable &&
|
||||
canRecoverAgentRegistration(application) &&
|
||||
hasAuth(AUGUST_PERMISSIONS.agentDistributionRegistration.recoverApproval)
|
||||
"
|
||||
type="warning"
|
||||
@@ -17,11 +17,25 @@
|
||||
</ElButton>
|
||||
</div>
|
||||
|
||||
<ElAlert
|
||||
v-if="application?.status === 3"
|
||||
title="审批提交失败,请申请人重新扫码申请"
|
||||
type="error"
|
||||
:closable="false"
|
||||
show-icon
|
||||
style="margin-bottom: 16px"
|
||||
/>
|
||||
|
||||
<ElDescriptions v-if="application" :column="2" border>
|
||||
<ElDescriptionsItem label="申请ID">{{ application.id }}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="业务状态">{{
|
||||
application.status_name || '-'
|
||||
}}</ElDescriptionsItem>
|
||||
<ElDescriptionsItem label="业务状态"
|
||||
><ElTag
|
||||
:type="
|
||||
application.status === 1 ? 'success' : application.status === 0 ? 'warning' : 'danger'
|
||||
"
|
||||
>{{ getAgentRegistrationStatusName(application) }}</ElTag
|
||||
></ElDescriptionsItem
|
||||
>
|
||||
<ElDescriptionsItem label="审批状态">{{
|
||||
application.approval_status_name || '-'
|
||||
}}</ElDescriptionsItem>
|
||||
@@ -79,6 +93,10 @@
|
||||
import { useAuth } from '@/composables/useAuth'
|
||||
import { formatDateTime } from '@/utils/business/format'
|
||||
import type { AgentDistributionRegistrationItem } from '@/types/api'
|
||||
import {
|
||||
canRecoverAgentRegistration,
|
||||
getAgentRegistrationStatusName
|
||||
} from '@/types/api/agentDistributionRegistration'
|
||||
|
||||
defineOptions({ name: 'AgentDistributionRegistrationDetail' })
|
||||
|
||||
@@ -102,7 +120,12 @@
|
||||
}
|
||||
|
||||
const recoverApproval = async () => {
|
||||
if (!application.value?.recoverable || recovering.value) return
|
||||
if (
|
||||
!canRecoverAgentRegistration(application.value) ||
|
||||
!hasAuth(AUGUST_PERMISSIONS.agentDistributionRegistration.recoverApproval) ||
|
||||
recovering.value
|
||||
)
|
||||
return
|
||||
try {
|
||||
await ElMessageBox.confirm('确定要恢复该代理注册申请的原审批提交吗?', '恢复审批', {
|
||||
type: 'warning',
|
||||
|
||||
@@ -36,6 +36,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
canRecoverAgentRegistration,
|
||||
getAgentRegistrationStatusName
|
||||
} from '@/types/api/agentDistributionRegistration'
|
||||
import { h, onMounted, reactive, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ElMessage, ElTag } from 'element-plus'
|
||||
@@ -74,7 +78,8 @@
|
||||
options: [
|
||||
{ label: '待审批', value: 0 },
|
||||
{ label: '已通过', value: 1 },
|
||||
{ label: '已驳回', value: 2 }
|
||||
{ label: '已驳回', value: 2 },
|
||||
{ label: '审批提交失败', value: 3 }
|
||||
],
|
||||
config: { clearable: true, placeholder: '请选择业务状态' }
|
||||
},
|
||||
@@ -129,8 +134,11 @@
|
||||
const formatStatus = (row: AgentDistributionRegistrationItem) =>
|
||||
h(
|
||||
ElTag,
|
||||
{ type: row.status === 1 ? 'success' : row.status === 2 ? 'danger' : 'warning' },
|
||||
() => row.status_name || '-'
|
||||
{
|
||||
type:
|
||||
row.status === 1 ? 'success' : row.status === 2 || row.status === 3 ? 'danger' : 'warning'
|
||||
},
|
||||
() => getAgentRegistrationStatusName(row)
|
||||
)
|
||||
|
||||
const { columnChecks, columns } = useCheckedColumns(() => [
|
||||
@@ -175,8 +183,8 @@
|
||||
label: '可恢复',
|
||||
width: 90,
|
||||
formatter: (row: AgentDistributionRegistrationItem) =>
|
||||
h(ElTag, { type: row.recoverable ? 'warning' : 'info' }, () =>
|
||||
row.recoverable ? '是' : '否'
|
||||
h(ElTag, { type: canRecoverAgentRegistration(row) ? 'warning' : 'info' }, () =>
|
||||
canRecoverAgentRegistration(row) ? '是' : '否'
|
||||
)
|
||||
},
|
||||
{
|
||||
|
||||
@@ -60,9 +60,9 @@
|
||||
<ElAlert type="info" :closable="false" show-icon class="import-alert">
|
||||
<template #title>导入说明</template>
|
||||
<div>通过 CSV 批量交接或清空店铺平台业务员。</div>
|
||||
<div>注意: 清空不要填写业务员登录账号</div>
|
||||
<div>平台登录账号只填写平台用户的登录手机号,不支持用户名;清空时该列留空。</div>
|
||||
<div>
|
||||
表头固定:<code>店铺编码,操作类型,业务员登录账号,备注</code>;操作类型仅支持「换绑」或「清空」。
|
||||
表头固定:<code>店铺编码,操作类型,平台登录账号,备注</code>;操作类型仅支持「换绑」或「清空」,旧表头不再支持。
|
||||
</div>
|
||||
<div>支持 UTF-8(可带 BOM),一行失败不影响其他行。</div>
|
||||
</ElAlert>
|
||||
@@ -191,6 +191,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { SHOP_BUSINESS_OWNER_CSV_TEMPLATE } from '@/utils/business/shopBusinessOwnerImport'
|
||||
import { computed, h, onMounted, onBeforeUnmount, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { UploadFilled } from '@element-plus/icons-vue'
|
||||
@@ -249,7 +250,7 @@
|
||||
prop: 'task_no',
|
||||
label: '任务编号',
|
||||
width: 160,
|
||||
fixed:true,
|
||||
fixed: true,
|
||||
showOverflowTooltip: true,
|
||||
formatter: (row: BusinessOwnerImportDetail) =>
|
||||
h(
|
||||
@@ -361,11 +362,15 @@
|
||||
const handleDownloadTemplate = () => {
|
||||
try {
|
||||
const link = document.createElement('a')
|
||||
link.href = new URL('@/template/批量换绑业务员模板.csv', import.meta.url).href
|
||||
const templateUrl = URL.createObjectURL(
|
||||
new Blob([SHOP_BUSINESS_OWNER_CSV_TEMPLATE], { type: 'text/csv;charset=utf-8' })
|
||||
)
|
||||
link.href = templateUrl
|
||||
link.download = '批量换绑业务员模板.csv'
|
||||
document.body.appendChild(link)
|
||||
link.click()
|
||||
document.body.removeChild(link)
|
||||
setTimeout(() => URL.revokeObjectURL(templateUrl), 0)
|
||||
ElMessage.success('模板下载成功')
|
||||
} catch (error) {
|
||||
console.error('下载模板失败:', error)
|
||||
|
||||
@@ -189,7 +189,8 @@
|
||||
<ElInput
|
||||
v-model="formData.init_password"
|
||||
type="password"
|
||||
placeholder="请输入初始账号密码"
|
||||
:placeholder="NEW_LOGIN_PASSWORD_HINT"
|
||||
maxlength="12"
|
||||
show-password
|
||||
/>
|
||||
</ElFormItem>
|
||||
@@ -384,6 +385,10 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
createNewLoginPasswordRule,
|
||||
NEW_LOGIN_PASSWORD_HINT
|
||||
} from '@/utils/validation/loginPassword'
|
||||
import { h } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import {
|
||||
@@ -1344,7 +1349,7 @@
|
||||
],
|
||||
init_password: [
|
||||
{ required: true, message: '请输入初始账号密码', trigger: 'blur' },
|
||||
{ min: 8, max: 32, message: '密码长度为 8-32 个字符', trigger: 'blur' }
|
||||
createNewLoginPasswordRule()
|
||||
],
|
||||
init_phone: [
|
||||
{ required: true, message: '请输入初始账号手机号', trigger: 'blur' },
|
||||
@@ -1389,7 +1394,11 @@
|
||||
if (formData.contact_name) data.contact_name = formData.contact_name
|
||||
if (formData.contact_phone) data.contact_phone = formData.contact_phone
|
||||
|
||||
await ShopService.createShop(data)
|
||||
const res = await ShopService.createShop(data)
|
||||
if (res.code !== 0) {
|
||||
ElMessage.error(res.msg || res.message || '店铺创建失败')
|
||||
return
|
||||
}
|
||||
ElMessage.success('新增成功')
|
||||
} else {
|
||||
const data: UpdateShopParams = {
|
||||
@@ -1423,8 +1432,8 @@
|
||||
await getShopList()
|
||||
// 提交成功后也重新加载顶级店铺
|
||||
await loadTopLevelShops()
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
} catch {
|
||||
// HTTP 层已提示,不输出带初始密码的请求错误对象。
|
||||
} finally {
|
||||
submitLoading.value = false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user