fix: 修改工单bug
All checks were successful
构建并部署前端到测试环境 / build-and-deploy (push) Successful in 2m15s

This commit is contained in:
sexygoat
2026-04-22 17:42:36 +08:00
parent 8378d2228e
commit fd22dc7ad4
14 changed files with 199 additions and 280 deletions

View File

@@ -280,6 +280,11 @@
transform: scale(0.6) !important; transform: scale(0.6) !important;
} }
} }
.el-checkbox__input.is-disabled .el-checkbox__inner {
background-color: var(--el-color-primary) !important;
border-color: var(--el-color-primary) !important;
}
} }
.el-notification .el-notification__icon { .el-notification .el-notification__icon {

Binary file not shown.

Binary file not shown.

View File

@@ -402,7 +402,7 @@
] ]
// 显示对话框 // 显示对话框
const showDialog = (type: string, row?: any) => { const showDialog = async (type: string, row?: any) => {
dialogVisible.value = true dialogVisible.value = true
dialogType.value = type dialogType.value = type
@@ -411,6 +411,10 @@
formRef.value.resetFields() formRef.value.resetFields()
} }
if (type === 'add') {
await loadAllRoles()
}
if (type === 'edit' && row) { if (type === 'edit' && row) {
formData.id = row.id formData.id = row.id
formData.username = row.username formData.username = row.username
@@ -590,7 +594,8 @@
try { try {
const params: any = { const params: any = {
page: 1, page: 1,
page_size: keyword ? 100 : 20 // 搜索时加载更多默认20条 page_size: keyword ? 100 : 20, // 搜索时加载更多默认20条
role_type: 1
} }
if (keyword) { if (keyword) {
params.role_name = keyword params.role_name = keyword
@@ -604,48 +609,14 @@
} }
} }
// 计算属性:新建账号时可选的角色(根据账号类型过滤) // 计算属性:新建账号时可选的角色
const availableRolesForCreate = computed(() => { const availableRolesForCreate = computed(() => {
let roles = allRoles.value return allRoles.value
// 根据账号类型过滤角色
if (formData.user_type === 1) {
// 超级管理员:不能分配角色
return []
} else if (formData.user_type === 2) {
// 平台用户:只显示平台角色
roles = roles.filter((role) => role.role_type === 1)
} else if (formData.user_type === 3) {
// 代理账号:只显示客户角色
roles = roles.filter((role) => role.role_type === 2)
} else if (formData.user_type === 4) {
// 企业账号:只显示客户角色
roles = roles.filter((role) => role.role_type === 2)
}
return roles
}) })
// 计算属性:过滤后的可分配角色(根据账号类型过滤) // 计算属性:过滤后的可分配角色
const filteredAvailableRoles = computed(() => { const filteredAvailableRoles = computed(() => {
let roles = allRoles.value return allRoles.value
// 根据账号类型过滤角色
if (currentAccountType.value === 1) {
// 超级管理员:不能分配任何角色
return []
} else if (currentAccountType.value === 3) {
// 代理账号:只显示客户角色
roles = roles.filter((role) => role.role_type === 2)
} else if (currentAccountType.value === 4) {
// 企业账号:只显示客户角色
roles = roles.filter((role) => role.role_type === 2)
} else if (currentAccountType.value === 2) {
// 平台用户:只显示平台角色
roles = roles.filter((role) => role.role_type === 1)
}
return roles
}) })
// 搜索防抖定时器 // 搜索防抖定时器

View File

@@ -6,9 +6,6 @@
<ElDescriptionsItem label="套餐名称"> <ElDescriptionsItem label="套餐名称">
{{ data.package_name }} {{ data.package_name }}
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="套餐使用记录ID">
{{ data.package_usage_id }}
</ElDescriptionsItem>
<ElDescriptionsItem label="总使用流量" :span="2"> <ElDescriptionsItem label="总使用流量" :span="2">
{{ formatDataSize(data.total_usage_mb) }} {{ formatDataSize(data.total_usage_mb) }}
</ElDescriptionsItem> </ElDescriptionsItem>

View File

@@ -171,20 +171,25 @@ export function useAssetInfo() {
const loadCurrentPackage = async (identifier: string) => { const loadCurrentPackage = async (identifier: string) => {
try { try {
currentPackageLoading.value = true currentPackageLoading.value = true
// 清空之前的错误信息
currentPackageErrorMsg.value = '' currentPackageErrorMsg.value = ''
const response = await AssetService.getCurrentPackage(identifier) const [currentPkgResponse, packageListResponse] = await Promise.all([
if (response.code === 0) { AssetService.getCurrentPackage(identifier),
if (response.data) { AssetService.getAssetPackages(identifier, { page: 1, page_size: 50 })
const pkg = response.data ])
const activePackage = packageListResponse.data?.items?.find((p: any) => p.status === 1)
const activePaidAmount = activePackage?.paid_amount || 0
if (currentPkgResponse.code === 0) {
if (currentPkgResponse.data) {
const pkg = currentPkgResponse.data
// 转换为组件期望的格式
currentPackage.value = { currentPackage.value = {
id: pkg.package_usage_id || 0, id: pkg.package_usage_id || 0,
package_id: pkg.package_id || 0, package_id: pkg.package_id || 0,
package_name: pkg.package_name || '', package_name: pkg.package_name || '',
package_price: 0, // API 没有返回价格 package_price: activePaidAmount,
package_total_data: pkg.data_limit_mb || 0, package_total_data: pkg.data_limit_mb || 0,
real_data_used: pkg.data_usage_mb || 0, real_data_used: pkg.data_usage_mb || 0,
real_data_remaining: (pkg.data_limit_mb || 0) - (pkg.data_usage_mb || 0), real_data_remaining: (pkg.data_limit_mb || 0) - (pkg.data_usage_mb || 0),
@@ -196,10 +201,10 @@ export function useAssetInfo() {
expire_time: pkg.expires_at || '', expire_time: pkg.expires_at || '',
status: pkg.status || 0, status: pkg.status || 0,
status_name: pkg.status_name, status_name: pkg.status_name,
duration_days: undefined, // API 没有返回时长 duration_days: undefined,
virtual_ratio: pkg.virtual_ratio, // 虚流量比例 virtual_ratio: pkg.virtual_ratio,
package_type: pkg.package_type, // 套餐类型 package_type: pkg.package_type,
enable_virtual_data: pkg.enable_virtual_data, // 是否启用虚流量 enable_virtual_data: pkg.enable_virtual_data,
data_limit_mb: pkg.data_limit_mb, data_limit_mb: pkg.data_limit_mb,
virtual_limit_mb: pkg.virtual_limit_mb, virtual_limit_mb: pkg.virtual_limit_mb,
virtual_remain_mb: pkg.virtual_remain_mb, virtual_remain_mb: pkg.virtual_remain_mb,

View File

@@ -338,34 +338,47 @@
label-width="80" label-width="80"
> >
<ElRow :gutter="20"> <ElRow :gutter="20">
<ElCol :span="10"> <ElCol :span="6">
<ElFormItem label="IoT卡" prop="iot_card_id"> <ElFormItem label="搜索类型" prop="search_type">
<ElSelect v-model="bindCardForm.search_type" placeholder="请选择搜索类型" style="width: 100%">
<ElOption label="ICCID" value="iccid" />
<ElOption label="运营商名称" value="carrier_name" />
</ElSelect>
</ElFormItem>
</ElCol>
<ElCol :span="8">
<ElFormItem label="ICCID" prop="iot_card_id">
<ElSelect <ElSelect
v-model="bindCardForm.iot_card_id" v-model="bindCardForm.iot_card_id"
:placeholder=" :placeholder="
iotCardList.length === 0 && !iotCardSearchLoading bindCardForm.search_type === 'carrier_name'
? '当前没有未绑定设备的卡' ? '请选择或搜索运营商名称'
: '请选择或搜索IoT卡支持ICCID搜索)' : '请选择或搜索ICCID'
" "
filterable filterable
remote remote
reserve-keyword reserve-keyword
:remote-method="searchIotCards" :remote-method="searchIotCards"
:loading="iotCardSearchLoading" :loading="iotCardSearchLoading"
:disabled="iotCardList.length === 0 && !iotCardSearchLoading"
clearable clearable
style="width: 100%" style="width: 100%"
> >
<ElOption <ElOption
v-for="card in iotCardList" v-for="card in iotCardList"
:key="card.id" :key="card.id"
:label="`${card.iccid} ${card.msisdn ? '(' + card.msisdn + ')' : ''}`" :label="`${card.iccid} ${card.carrier_name ? '(' + card.carrier_name + ')' : ''}`"
:value="card.id" :value="card.id"
/> />
<ElOption
v-if="iotCardList.length === 0 && !iotCardSearchLoading"
label="未找到相关数据"
:value="undefined"
disabled
/>
</ElSelect> </ElSelect>
</ElFormItem> </ElFormItem>
</ElCol> </ElCol>
<ElCol :span="10"> <ElCol :span="6">
<ElFormItem label="插槽位置" prop="slot_position"> <ElFormItem label="插槽位置" prop="slot_position">
<ElSelect <ElSelect
v-model="bindCardForm.slot_position" v-model="bindCardForm.slot_position"
@@ -561,7 +574,8 @@
ShopService, ShopService,
CardService, CardService,
PackageSeriesService, PackageSeriesService,
AssetService AssetService,
CarrierService
} from '@/api/modules' } from '@/api/modules'
import { ElMessage, ElMessageBox, ElTag, ElIcon, ElRadioGroup, ElRadio } from 'element-plus' import { ElMessage, ElMessageBox, ElTag, ElIcon, ElRadioGroup, ElRadio } from 'element-plus'
import { Loading } from '@element-plus/icons-vue' import { Loading } from '@element-plus/icons-vue'
@@ -656,11 +670,13 @@
const iotCardSearchLoading = ref(false) const iotCardSearchLoading = ref(false)
const iotCardHasSearched = ref(false) // 标记是否已执行过搜索 const iotCardHasSearched = ref(false) // 标记是否已执行过搜索
const bindCardForm = reactive({ const bindCardForm = reactive({
search_type: 'iccid' as 'iccid' | 'carrier_name',
iot_card_id: undefined as number | undefined, iot_card_id: undefined as number | undefined,
slot_position: 1 slot_position: 1
}) })
const bindCardRules = reactive<FormRules>({ const bindCardRules = reactive<FormRules>({
iot_card_id: [{ required: true, message: '请选择IoT卡', trigger: 'change' }], search_type: [{ required: true, message: '请选择搜索类型', trigger: 'change' }],
iot_card_id: [{ required: true, message: '请选择ICCID', trigger: 'change' }],
slot_position: [{ required: true, message: '请选择插槽位置', trigger: 'change' }] slot_position: [{ required: true, message: '请选择插槽位置', trigger: 'change' }]
}) })
@@ -855,10 +871,14 @@
deviceCards.value = [] deviceCards.value = []
deviceCardsDialogVisible.value = true deviceCardsDialogVisible.value = true
// 重置绑定卡表单 // 重置绑定卡表单
bindCardForm.search_type = 'iccid'
bindCardForm.iot_card_id = undefined bindCardForm.iot_card_id = undefined
bindCardForm.slot_position = 1 bindCardForm.slot_position = 1
// 加载设备卡列表 // 加载未绑定设备卡列表
await loadDeviceCards(device.virtual_no) await Promise.all([
loadDeviceCards(device.virtual_no),
loadDefaultIotCards()
])
} }
// 加载设备绑定的卡列表 // 加载设备绑定的卡列表
@@ -897,7 +917,7 @@
} }
} }
// 搜索IoT卡根据ICCID // 搜索IoT卡根据ICCID或运营商名称
const searchIotCards = async (query: string) => { const searchIotCards = async (query: string) => {
if (!query) { if (!query) {
await loadDefaultIotCards() await loadDefaultIotCards()
@@ -906,12 +926,17 @@
iotCardSearchLoading.value = true iotCardSearchLoading.value = true
iotCardHasSearched.value = true iotCardHasSearched.value = true
try { try {
const res = await CardService.getStandaloneIotCards({ const params: any = {
page: 1, page: 1,
page_size: 20, page_size: 20,
iccid: query,
is_standalone: true is_standalone: true
}) }
if (bindCardForm.search_type === 'iccid') {
params.iccid = query
} else if (bindCardForm.search_type === 'carrier_name') {
params.carrier_name = query
}
const res = await CardService.getStandaloneIotCards(params)
if (res.code === 0 && res.data?.items) { if (res.code === 0 && res.data?.items) {
iotCardList.value = res.data.items iotCardList.value = res.data.items
} }

View File

@@ -562,7 +562,7 @@
import { h } from 'vue' import { h } from 'vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { RoutesAlias } from '@/router/routesAlias' import { RoutesAlias } from '@/router/routesAlias'
import { CardService, ShopService, PackageSeriesService, AssetService } from '@/api/modules' import { CardService, ShopService, PackageSeriesService, AssetService, CarrierService } from '@/api/modules'
import { ElMessage, ElTag, ElIcon, ElMessageBox } from 'element-plus' import { ElMessage, ElTag, ElIcon, ElMessageBox } from 'element-plus'
import { Loading } from '@element-plus/icons-vue' import { Loading } from '@element-plus/icons-vue'
import RealnamePolicyDialog from '@/components/device/RealnamePolicyDialog.vue' import RealnamePolicyDialog from '@/components/device/RealnamePolicyDialog.vue'
@@ -627,6 +627,38 @@
failed_items: null failed_items: null
}) })
// 运营商搜索相关
const carrierLoading = ref(false)
const searchCarrierOptions = ref<any[]>([])
// 加载搜索栏运营商选项默认加载10条
const loadSearchCarrierOptions = async (carrierName?: string) => {
try {
const params: any = {
page: 1,
page_size: 10
}
if (carrierName) {
params.carrier_name = carrierName
}
const res = await CarrierService.getCarriers(params)
if (res.code === 0) {
searchCarrierOptions.value = res.data.items
}
} catch (error) {
console.error('加载运营商选项失败:', error)
}
}
// 搜索运营商(用于搜索栏)
const handleSearchCarrier = (query: string) => {
if (query) {
loadSearchCarrierOptions(query)
} else {
loadSearchCarrierOptions()
}
}
// 卡详情弹窗相关 // 卡详情弹窗相关
const cardDetailDialogVisible = ref(false) const cardDetailDialogVisible = ref(false)
const cardDetailLoading = ref(false) const cardDetailLoading = ref(false)
@@ -730,8 +762,8 @@
// 搜索表单初始值 // 搜索表单初始值
const initialSearchState: { const initialSearchState: {
status: undefined | number status: undefined | number
carrier_id: undefined | number
iccid: string iccid: string
carrier_name: string
msisdn: string msisdn: string
virtual_no: string virtual_no: string
device_virtual_no: string device_virtual_no: string
@@ -740,8 +772,8 @@
[key: string]: any [key: string]: any
} = { } = {
status: undefined, status: undefined,
carrier_id: undefined,
iccid: '', iccid: '',
carrier_name: '',
msisdn: '', msisdn: '',
virtual_no: '', virtual_no: '',
device_virtual_no: '', device_virtual_no: '',
@@ -863,20 +895,6 @@
{ label: '已停用', value: 4 } { label: '已停用', value: 4 }
] ]
}, },
{
label: '运营商',
prop: 'carrier_id',
type: 'select',
config: {
clearable: true,
placeholder: '全部'
},
options: () => [
{ label: '中国移动', value: 1 },
{ label: '中国联通', value: 2 },
{ label: '中国电信', value: 3 }
]
},
{ {
label: 'ICCID', label: 'ICCID',
prop: 'iccid', prop: 'iccid',
@@ -886,6 +904,24 @@
placeholder: '请输入ICCID' placeholder: '请输入ICCID'
} }
}, },
{
label: '运营商名称',
prop: 'carrier_name',
type: 'select',
config: {
clearable: true,
filterable: true,
remote: true,
remoteMethod: handleSearchCarrier,
loading: carrierLoading.value,
placeholder: '请选择或搜索运营商名称'
},
options: () =>
searchCarrierOptions.value.map((c) => ({
label: c.carrier_name,
value: c.carrier_name
}))
},
{ {
label: '卡接入号', label: '卡接入号',
prop: 'msisdn', prop: 'msisdn',
@@ -1180,20 +1216,22 @@
const getActions = (row: StandaloneIotCard) => { const getActions = (row: StandaloneIotCard) => {
const actions: any[] = [] const actions: any[] = []
if (hasAuth('iot_card:start_card')) { if (row.network_status === 1) {
actions.push({ if (hasAuth('iot_card:stop_card')) {
label: '启用此卡', actions.push({
handler: () => handleCardOperation('start-card', row.iccid), label: '停用此卡',
type: 'primary' handler: () => handleCardOperation('stop-card', row.iccid),
}) type: 'primary'
} })
}
if (hasAuth('iot_card:stop_card')) { } else {
actions.push({ if (hasAuth('iot_card:start_card')) {
label: '停用此卡', actions.push({
handler: () => handleCardOperation('stop-card', row.iccid), label: '启用此卡',
type: 'primary' handler: () => handleCardOperation('start-card', row.iccid),
}) type: 'primary'
})
}
} }
if (hasAuth('iot_card:realname_policy')) { if (hasAuth('iot_card:realname_policy')) {
@@ -1216,6 +1254,7 @@
} }
onMounted(() => { onMounted(() => {
loadSearchCarrierOptions()
getTableData() getTableData()
}) })
@@ -1228,7 +1267,6 @@
page_size: pagination.pageSize, page_size: pagination.pageSize,
...formFilters ...formFilters
} }
// 清理空值
Object.keys(params).forEach((key) => { Object.keys(params).forEach((key) => {
if (params[key] === '' || params[key] === undefined) { if (params[key] === '' || params[key] === undefined) {
delete params[key] delete params[key]

View File

@@ -61,7 +61,7 @@
<p>2. 仅支持 Excel 格式.xlsx单次最多导入 1000 </p> <p>2. 仅支持 Excel 格式.xlsx单次最多导入 1000 </p>
<p>3. 列格式请设置为文本格式避免长数字被转为科学计数法</p> <p>3. 列格式请设置为文本格式避免长数字被转为科学计数法</p>
<p>4. <strong>重要列顺序固定不可调整</strong>系统按位置读取不识别列名</p> <p>4. <strong>重要列顺序固定不可调整</strong>系统按位置读取不识别列名</p>
<p>5. 必填列虚拟号第1列</p> <p style="color: var(--el-color-primary)">5. 必填列虚拟号第1列</p>
<p <p
>6. >6.
可选列设备名称设备型号设备类型IMEI制造商最大SIM槽数默认4有效范围1-4卡1~卡4 可选列设备名称设备型号设备类型IMEI制造商最大SIM槽数默认4有效范围1-4卡1~卡4
@@ -443,77 +443,16 @@
// 下载模板 // 下载模板
const downloadTemplate = async () => { const downloadTemplate = async () => {
try { try {
// 动态导入 xlsx 库 const link = document.createElement('a')
const XLSX = await import('xlsx') link.href = new URL('@/template/设备导入模板.xlsx', import.meta.url).href
link.download = '设备导入模板.xlsx'
// 创建示例数据(按固定列顺序) document.body.appendChild(link)
const templateData = [ link.click()
{ document.body.removeChild(link)
虚拟号: '862639070731999',
设备名称: '智能水表01',
设备型号: 'WM-2000',
设备类型: '智能水表',
IMEI: '860123456789012',
制造商: '华为',
最大SIM槽数: 4,
'卡1 ICCID': '89860123456789012345',
'卡2 ICCID': '',
'卡3 ICCID': '',
'卡4 ICCID': ''
},
{
虚拟号: '862639070750932',
设备名称: 'GPS定位器01',
设备型号: 'GPS-3000',
设备类型: '定位设备',
IMEI: '860123456789013',
制造商: '小米',
最大SIM槽数: 2,
'卡1 ICCID': '89860123456789012346',
'卡2 ICCID': '89860123456789012347',
'卡3 ICCID': '',
'卡4 ICCID': ''
}
]
// 创建工作簿
const wb = XLSX.utils.book_new()
const ws = XLSX.utils.json_to_sheet(templateData)
// 设置列宽
ws['!cols'] = [
{ wch: 20 }, // 虚拟号
{ wch: 15 }, // 设备名称
{ wch: 15 }, // 设备型号
{ wch: 15 }, // 设备类型
{ wch: 18 }, // IMEI
{ wch: 12 }, // 制造商
{ wch: 15 }, // 最大SIM槽数
{ wch: 25 }, // 卡1 ICCID
{ wch: 25 }, // 卡2 ICCID
{ wch: 25 }, // 卡3 ICCID
{ wch: 25 } // 卡4 ICCID
]
// 将所有单元格设置为文本格式
const range = XLSX.utils.decode_range(ws['!ref'] || 'A1')
for (let R = range.s.r; R <= range.e.r; ++R) {
for (let C = range.s.c; C <= range.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: R, c: C })
if (!ws[cellAddress]) continue
ws[cellAddress].t = 's' // 设置为字符串类型
}
}
// 添加工作表到工作簿
XLSX.utils.book_append_sheet(wb, ws, '设备导入模板')
// 导出文件
XLSX.writeFile(wb, '设备导入模板.xlsx')
ElMessage.success('设备导入模板下载成功') ElMessage.success('设备导入模板下载成功')
} catch (error) { } catch (error) {
console.error('下载模板失败:', error) console.error('下载模板失败:', error)
ElMessage.error('下载模板失败')
} }
} }

View File

@@ -105,10 +105,8 @@
<p>1. 请先下载 Excel 模板文件按照模板格式填写IoT卡信息</p> <p>1. 请先下载 Excel 模板文件按照模板格式填写IoT卡信息</p>
<p>2. 仅支持 Excel 格式.xlsx单次最多导入 1000 </p> <p>2. 仅支持 Excel 格式.xlsx单次最多导入 1000 </p>
<p>3. 列格式请设置为文本格式避免长数字被转为科学计数法</p> <p>3. 列格式请设置为文本格式避免长数字被转为科学计数法</p>
<p>4. <strong>必填字段ICCIDMSISDN手机号virtual_no虚拟号</strong></p> <p>4. <span style="color: var(--el-color-primary)">必填字段ICCIDMSISDN</span></p>
<p>5. <strong>Excel 文件必须包含 virtual_no 否则整批导入将失败</strong></p> <p>5. 必须选择运营商</p>
<p>6. virtual_no 为空将导入失败</p>
<p>7. 必须选择运营商</p>
</div> </div>
</template> </template>
</ElAlert> </ElAlert>
@@ -642,58 +640,16 @@
// 下载模板 // 下载模板
const downloadTemplate = async () => { const downloadTemplate = async () => {
try { try {
// 动态导入 xlsx 库 const link = document.createElement('a')
const XLSX = await import('xlsx') link.href = new URL('@/template/IoT卡导入模板.xlsx', import.meta.url).href
link.download = 'IoT卡导入模板.xlsx'
// 创建示例数据按固定列顺序ICCID、MSISDN、虚拟号 document.body.appendChild(link)
const templateData = [ link.click()
{ document.body.removeChild(link)
ICCID: '89860123456789012345',
'MSISDN接入号': '13800138000',
虚拟号: 'V001'
},
{
ICCID: '89860123456789012346',
'MSISDN接入号': '13800138001',
虚拟号: 'V002'
},
{
ICCID: '89860123456789012347',
'MSISDN接入号': '13800138002',
虚拟号: 'V003'
}
]
// 创建工作簿
const wb = XLSX.utils.book_new()
const ws = XLSX.utils.json_to_sheet(templateData)
// 设置列宽
ws['!cols'] = [
{ wch: 25 }, // ICCID
{ wch: 20 }, // MSISDN接入号
{ wch: 15 } // 虚拟号
]
// 将所有单元格设置为文本格式,防止科学计数法
const range = XLSX.utils.decode_range(ws['!ref'] || 'A1')
for (let R = range.s.r; R <= range.e.r; ++R) {
for (let C = range.s.c; C <= range.e.c; ++C) {
const cellAddress = XLSX.utils.encode_cell({ r: R, c: C })
if (!ws[cellAddress]) continue
ws[cellAddress].t = 's' // 设置为字符串类型
}
}
// 添加工作表到工作簿
XLSX.utils.book_append_sheet(wb, ws, 'IoT卡导入模板')
// 导出文件
XLSX.writeFile(wb, 'IoT卡导入模板.xlsx')
ElMessage.success('IoT卡导入模板下载成功') ElMessage.success('IoT卡导入模板下载成功')
} catch (error) { } catch (error) {
console.error('下载模板失败:', error) console.error('下载模板失败:', error)
ElMessage.error('下载模板失败')
} }
} }

View File

@@ -582,7 +582,8 @@
try { try {
const params: any = { const params: any = {
page: 1, page: 1,
page_size: 20 page_size: 20,
role_type: 1
} }
if (keyword) { if (keyword) {
params.role_name = keyword params.role_name = keyword
@@ -607,23 +608,9 @@
}, 300) }, 300)
} }
// 计算属性:过滤后的可分配角色(根据账号类型过滤) // 计算属性:过滤后的可分配角色
const filteredAvailableRoles = computed(() => { const filteredAvailableRoles = computed(() => {
let roles = allRoles.value return allRoles.value
// 根据账号类型过滤角色
if (currentAccountType.value === 3) {
// 代理账号:只显示客户角色
roles = roles.filter((role) => role.role_type === 2)
} else if (currentAccountType.value === 2) {
// 平台用户:只显示平台角色
roles = roles.filter((role) => role.role_type === 1)
} else if (currentAccountType.value === 4) {
// 企业账号:只显示客户角色
roles = roles.filter((role) => role.role_type === 2)
}
return roles
}) })
// 计算属性:过滤后的已分配角色 // 计算属性:过滤后的已分配角色

View File

@@ -94,9 +94,7 @@
title: '订单信息', title: '订单信息',
fields: [ fields: [
{ label: '充值单号', prop: 'recharge_no' }, { label: '充值单号', prop: 'recharge_no' },
{ label: '充值记录ID', prop: 'id' },
{ label: '店铺名称', prop: 'shop_name' }, { label: '店铺名称', prop: 'shop_name' },
{ label: '店铺ID', prop: 'shop_id' },
{ {
label: '充值金额', label: '充值金额',
formatter: (_, data) => formatCurrency(data.amount) formatter: (_, data) => formatCurrency(data.amount)
@@ -122,15 +120,6 @@
prop: 'payment_channel', prop: 'payment_channel',
formatter: (value) => value || '-' formatter: (value) => value || '-'
}, },
{
label: '支付配置ID',
prop: 'payment_config_id',
formatter: (value) => value || '-'
},
{
label: '代理钱包ID',
prop: 'agent_wallet_id'
},
{ {
label: '第三方支付流水号', label: '第三方支付流水号',
prop: 'payment_transaction_id', prop: 'payment_transaction_id',

View File

@@ -14,12 +14,6 @@
<ElTag :type="getStatusType(refund.status)">{{ getStatusText(refund.status) }}</ElTag> <ElTag :type="getStatusType(refund.status)">{{ getStatusText(refund.status) }}</ElTag>
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="订单ID">{{ refund.order_id }}</ElDescriptionsItem>
<ElDescriptionsItem label="店铺ID">{{ refund.shop_id }}</ElDescriptionsItem>
<ElDescriptionsItem label="套餐使用记录ID">
{{ refund.package_usage_id || '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="申请退款金额"> <ElDescriptionsItem label="申请退款金额">
{{ formatCurrency(refund.requested_refund_amount) }} {{ formatCurrency(refund.requested_refund_amount) }}
</ElDescriptionsItem> </ElDescriptionsItem>
@@ -42,34 +36,29 @@
</ElTag> </ElTag>
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="退款原因" :span="2"> <ElDescriptionsItem label="退款原因">
{{ refund.refund_reason || '-' }} {{ refund.refund_reason || '-' }}
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="拒绝原因" :span="2"> <ElDescriptionsItem label="拒绝原因">
{{ refund.reject_reason || '-' }} {{ refund.reject_reason || '-' }}
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="审批备注" :span="2"> <ElDescriptionsItem label="审批时间">
{{ refund.remark || '-' }} {{ refund.processed_at ? formatDateTime(refund.processed_at) : '-' }}
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="创建人ID">{{ refund.creator }}</ElDescriptionsItem> <ElDescriptionsItem label="审批备注">
<ElDescriptionsItem label="审批人ID"> {{ refund.remark || '-' }}
{{ refund.processor_id || '-' }}
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="创建时间"> <ElDescriptionsItem label="创建时间">
{{ formatDateTime(refund.created_at) }} {{ formatDateTime(refund.created_at) }}
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="审批时间">
{{ refund.processed_at ? formatDateTime(refund.processed_at) : '-' }}
</ElDescriptionsItem>
<ElDescriptionsItem label="更新时间"> <ElDescriptionsItem label="更新时间">
{{ formatDateTime(refund.updated_at) }} {{ formatDateTime(refund.updated_at) }}
</ElDescriptionsItem> </ElDescriptionsItem>
<ElDescriptionsItem label="更新人ID">{{ refund.updater }}</ElDescriptionsItem>
</ElDescriptions> </ElDescriptions>
<div class="action-buttons" v-if="refund"> <div class="action-buttons" v-if="refund">

View File

@@ -63,11 +63,12 @@
placeholder="请输入订单号搜索" placeholder="请输入订单号搜索"
style="width: 100%" style="width: 100%"
@focus="handleOrderSelectFocus" @focus="handleOrderSelectFocus"
@change="handleOrderSelectChange"
> >
<ElOption <ElOption
v-for="order in orderSearchOptions" v-for="order in orderSearchOptions"
:key="order.id" :key="order.id"
:label="`订单号: ${order.order_no} (ID: ${order.id})`" :label="order.order_no"
:value="order.id" :value="order.id"
/> />
</ElSelect> </ElSelect>
@@ -75,21 +76,18 @@
<ElFormItem label="申请退款金额" prop="requested_refund_amount"> <ElFormItem label="申请退款金额" prop="requested_refund_amount">
<ElInputNumber <ElInputNumber
v-model="createForm.requested_refund_amount" v-model="createForm.requested_refund_amount"
:min="1" :min="0"
:precision="2" :precision="2"
:step="1" :step="1"
style="width: 100%" style="width: 100%"
placeholder="请输入申请退款金额(元)" placeholder="请输入申请退款金额(元)"
/> />
</ElFormItem> </ElFormItem>
<ElFormItem label="实收金额" prop="actual_received_amount"> <ElFormItem label="实收金额">
<ElInputNumber <ElInput
v-model="createForm.actual_received_amount" :model-value="selectedOrderActualPaid"
:min="1" disabled
:precision="2"
:step="1"
style="width: 100%" style="width: 100%"
placeholder="请输入实收金额(元)"
/> />
</ElFormItem> </ElFormItem>
<ElFormItem label="退款原因"> <ElFormItem label="退款原因">
@@ -434,8 +432,7 @@
const createRules = reactive<FormRules>({ const createRules = reactive<FormRules>({
order_id: [{ required: true, message: '请输入订单ID', trigger: 'blur' }], order_id: [{ required: true, message: '请输入订单ID', trigger: 'blur' }],
requested_refund_amount: [{ required: true, message: '请输入申请退款金额', trigger: 'blur' }], requested_refund_amount: [{ required: true, message: '请输入申请退款金额', trigger: 'blur' }]
actual_received_amount: [{ required: true, message: '请输入实收金额', trigger: 'blur' }]
}) })
const approveRules = reactive<FormRules>({}) const approveRules = reactive<FormRules>({})
@@ -485,6 +482,15 @@
const refundList = ref<Refund[]>([]) const refundList = ref<Refund[]>([])
// 选中的订单实收金额
const selectedOrderActualPaid = computed(() => {
if (!createForm.order_id) return 0
const selectedOrder = orderSearchOptions.value.find((o) => o.id === createForm.order_id)
if (!selectedOrder?.actual_paid_amount) return 0
const amount = selectedOrder.actual_paid_amount / 100
return isNaN(amount) ? 0 : amount
})
// 格式化货币 - 将分转换为元 // 格式化货币 - 将分转换为元
const formatCurrency = (amount: number): string => { const formatCurrency = (amount: number): string => {
return `¥${(amount / 100).toFixed(2)}` return `¥${(amount / 100).toFixed(2)}`
@@ -684,6 +690,18 @@
} }
} }
// 订单选择变化 - 自动填充实收金额
const handleOrderSelectChange = (orderId: number | null) => {
if (!orderId) {
createForm.actual_received_amount = 0
return
}
const selectedOrder = orderSearchOptions.value.find((o) => o.id === orderId)
if (selectedOrder) {
createForm.actual_received_amount = selectedOrder.actual_paid_amount / 100
}
}
// 获取退款申请列表 // 获取退款申请列表
const getTableData = async () => { const getTableData = async () => {
loading.value = true loading.value = true