add
This commit is contained in:
97
components.d.ts
vendored
97
components.d.ts
vendored
@@ -0,0 +1,97 @@
|
|||||||
|
/* eslint-disable */
|
||||||
|
// @ts-nocheck
|
||||||
|
// Generated by unplugin-vue-components
|
||||||
|
// Read more: https://github.com/vuejs/core/pull/3399
|
||||||
|
// biome-ignore lint: disable
|
||||||
|
export {}
|
||||||
|
|
||||||
|
/* prettier-ignore */
|
||||||
|
declare module 'vue' {
|
||||||
|
export interface GlobalComponents {
|
||||||
|
AppBreadcrumb: typeof import('./src/components/layout/AppBreadcrumb.vue')['default']
|
||||||
|
AppHeader: typeof import('./src/components/layout/AppHeader.vue')['default']
|
||||||
|
AppLoading: typeof import('./src/components/common/AppLoading.vue')['default']
|
||||||
|
AppSidebar: typeof import('./src/components/layout/AppSidebar.vue')['default']
|
||||||
|
BusinessConsultationDialog: typeof import('./src/components/common/BusinessConsultationDialog.vue')['default']
|
||||||
|
CertificationBanner: typeof import('./src/components/common/CertificationBanner.vue')['default']
|
||||||
|
CertificationNotice: typeof import('./src/components/common/CertificationNotice.vue')['default']
|
||||||
|
ChartCard: typeof import('./src/components/statistics/ChartCard.vue')['default']
|
||||||
|
CodeDisplay: typeof import('./src/components/common/CodeDisplay.vue')['default']
|
||||||
|
CustomSteps: typeof import('./src/components/common/CustomSteps.vue')['default']
|
||||||
|
DanmakuBar: typeof import('./src/components/common/DanmakuBar.vue')['default']
|
||||||
|
ElAlert: typeof import('element-plus/es')['ElAlert']
|
||||||
|
ElAside: typeof import('element-plus/es')['ElAside']
|
||||||
|
ElAvatar: typeof import('element-plus/es')['ElAvatar']
|
||||||
|
ElButton: typeof import('element-plus/es')['ElButton']
|
||||||
|
ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
|
||||||
|
ElCard: typeof import('element-plus/es')['ElCard']
|
||||||
|
ElCol: typeof import('element-plus/es')['ElCol']
|
||||||
|
ElColorPicker: typeof import('element-plus/es')['ElColorPicker']
|
||||||
|
ElContainer: typeof import('element-plus/es')['ElContainer']
|
||||||
|
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']
|
||||||
|
ElHeader: typeof import('element-plus/es')['ElHeader']
|
||||||
|
ElIcon: typeof import('element-plus/es')['ElIcon']
|
||||||
|
ElInput: typeof import('element-plus/es')['ElInput']
|
||||||
|
ElInputNumber: typeof import('element-plus/es')['ElInputNumber']
|
||||||
|
ElLoading: typeof import('element-plus/es')['ElLoading']
|
||||||
|
ElMain: typeof import('element-plus/es')['ElMain']
|
||||||
|
ElMenu: typeof import('element-plus/es')['ElMenu']
|
||||||
|
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
|
||||||
|
ElOption: typeof import('element-plus/es')['ElOption']
|
||||||
|
ElPagination: typeof import('element-plus/es')['ElPagination']
|
||||||
|
ElRadio: typeof import('element-plus/es')['ElRadio']
|
||||||
|
ElRadioButton: typeof import('element-plus/es')['ElRadioButton']
|
||||||
|
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
|
||||||
|
ElRow: typeof import('element-plus/es')['ElRow']
|
||||||
|
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']
|
||||||
|
ElTimePicker: typeof import('element-plus/es')['ElTimePicker']
|
||||||
|
ElTooltip: typeof import('element-plus/es')['ElTooltip']
|
||||||
|
ElTree: typeof import('element-plus/es')['ElTree']
|
||||||
|
ElUpload: typeof import('element-plus/es')['ElUpload']
|
||||||
|
ExportDialog: typeof import('./src/components/common/ExportDialog.vue')['default']
|
||||||
|
FileUpload: typeof import('./src/components/common/FileUpload.vue')['default']
|
||||||
|
FilterItem: typeof import('./src/components/common/FilterItem.vue')['default']
|
||||||
|
FilterSection: typeof import('./src/components/common/FilterSection.vue')['default']
|
||||||
|
FloatingCustomerService: typeof import('./src/components/common/FloatingCustomerService.vue')['default']
|
||||||
|
HelloWorld: typeof import('./src/components/HelloWorld.vue')['default']
|
||||||
|
ListPageLayout: typeof import('./src/components/common/ListPageLayout.vue')['default']
|
||||||
|
MarkdownEditor: typeof import('./src/components/common/MarkdownEditor.vue')['default']
|
||||||
|
NotificationPanel: typeof import('./src/components/layout/NotificationPanel.vue')['default']
|
||||||
|
PermissionGuard: typeof import('./src/components/auth/PermissionGuard.vue')['default']
|
||||||
|
ProductApiConfigDialog: typeof import('./src/components/admin/ProductApiConfigDialog.vue')['default']
|
||||||
|
ProductCard: typeof import('./src/components/product/ProductCard.vue')['default']
|
||||||
|
ProductDocumentationDialog: typeof import('./src/components/admin/ProductDocumentationDialog.vue')['default']
|
||||||
|
ProductFormDialog: typeof import('./src/components/admin/ProductFormDialog.vue')['default']
|
||||||
|
ResponsiveActionColumn: typeof import('./src/components/common/ResponsiveActionColumn.vue')['default']
|
||||||
|
RichTextEditor: typeof import('./src/components/common/RichTextEditor.vue')['default']
|
||||||
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
StatCard: typeof import('./src/components/statistics/StatCard.vue')['default']
|
||||||
|
StatisticsDashboard: typeof import('./src/components/statistics/StatisticsDashboard.vue')['default']
|
||||||
|
TheWelcome: typeof import('./src/components/TheWelcome.vue')['default']
|
||||||
|
VersionInfo: typeof import('./src/components/common/VersionInfo.vue')['default']
|
||||||
|
WelcomeItem: typeof import('./src/components/WelcomeItem.vue')['default']
|
||||||
|
}
|
||||||
|
export interface GlobalDirectives {
|
||||||
|
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
99
src/api/ui-component.js
Normal file
99
src/api/ui-component.js
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
export const uiComponentApi = {
|
||||||
|
// 获取UI组件列表
|
||||||
|
getUIComponentList(params) {
|
||||||
|
return request.get('/admin/ui-components', { params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取UI组件详情
|
||||||
|
getUIComponentDetail(id) {
|
||||||
|
return request.get(`/admin/ui-components/${id}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 创建UI组件
|
||||||
|
createUIComponent(data) {
|
||||||
|
// 确保发送的数据结构与后端期望的完全匹配
|
||||||
|
const requestData = {
|
||||||
|
component_code: data.component_code || '',
|
||||||
|
component_name: data.component_name || '',
|
||||||
|
description: data.description || '',
|
||||||
|
version: data.version || '',
|
||||||
|
is_active: data.is_active !== undefined ? data.is_active : true,
|
||||||
|
sort_order: data.sort_order !== undefined ? data.sort_order : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加调试日志
|
||||||
|
console.log('创建UI组件请求数据:', requestData)
|
||||||
|
|
||||||
|
return request.post('/admin/ui-components', requestData)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新UI组件
|
||||||
|
updateUIComponent(id, data) {
|
||||||
|
// 确保发送的数据结构与后端期望的完全匹配
|
||||||
|
const requestData = {
|
||||||
|
id: id,
|
||||||
|
component_code: data.component_code || '',
|
||||||
|
component_name: data.component_name || '',
|
||||||
|
description: data.description || '',
|
||||||
|
version: data.version || '',
|
||||||
|
is_active: data.is_active !== undefined ? data.is_active : true,
|
||||||
|
sort_order: data.sort_order !== undefined ? data.sort_order : 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加调试日志
|
||||||
|
console.log('更新UI组件请求数据:', requestData)
|
||||||
|
|
||||||
|
return request.put(`/admin/ui-components/${id}`, requestData)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除UI组件
|
||||||
|
deleteUIComponent(id) {
|
||||||
|
return request.delete(`/admin/ui-components/${id}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传UI组件文件
|
||||||
|
uploadUIComponentFile(id, formData) {
|
||||||
|
return request.post(`/admin/ui-components/${id}/upload`, formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 上传并解压UI组件文件
|
||||||
|
uploadAndExtractUIComponentFile(id, formData) {
|
||||||
|
return request.post(`/admin/ui-components/${id}/upload-extract`, formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 下载UI组件文件
|
||||||
|
downloadUIComponentFile(id) {
|
||||||
|
return request.get(`/admin/ui-components/${id}/download`, {
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取UI组件文件夹内容
|
||||||
|
getUIComponentFolderContent(id) {
|
||||||
|
return request.get(`/admin/ui-components/${id}/folder-content`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除UI组件文件夹
|
||||||
|
deleteUIComponentFolder(id) {
|
||||||
|
return request.delete(`/admin/ui-components/${id}/folder`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 创建UI组件并上传文件(合并操作)
|
||||||
|
createUIComponentWithFile(formData) {
|
||||||
|
return request.post('/admin/ui-components/create-with-file', formData, {
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'multipart/form-data'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -124,7 +124,8 @@ export const getUserAccessibleMenuItems = (userType = 'user') => {
|
|||||||
{ name: '调用记录', path: '/admin/usage', icon: Clipboard },
|
{ name: '调用记录', path: '/admin/usage', icon: Clipboard },
|
||||||
{ name: '消费记录', path: '/admin/transactions', icon: Clipboard },
|
{ name: '消费记录', path: '/admin/transactions', icon: Clipboard },
|
||||||
{ name: '充值记录', path: '/admin/recharge-records', icon: CreditCard },
|
{ name: '充值记录', path: '/admin/recharge-records', icon: CreditCard },
|
||||||
{ name: '发票管理', path: '/admin/invoices', icon: Wallet }
|
{ name: '发票管理', path: '/admin/invoices', icon: Wallet },
|
||||||
|
{ name: '组件管理', path: '/admin/ui-components', icon: Cube }
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,7 +73,7 @@
|
|||||||
上传文件
|
上传文件
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
v-if="row.file_path && !row.is_extracted"
|
v-if="row.file_path && !row.is_extracted && isZipFileFromPath(row.file_path)"
|
||||||
size="small"
|
size="small"
|
||||||
type="warning"
|
type="warning"
|
||||||
@click="handleUploadExtract(row)"
|
@click="handleUploadExtract(row)"
|
||||||
@@ -162,6 +162,36 @@
|
|||||||
<el-form-item label="排序">
|
<el-form-item label="排序">
|
||||||
<el-input-number v-model="form.sort_order" :min="0" />
|
<el-input-number v-model="form.sort_order" :min="0" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="上传模式" v-if="!isEdit">
|
||||||
|
<el-radio-group v-model="uploadMode">
|
||||||
|
<el-radio label="files">文件上传</el-radio>
|
||||||
|
<el-radio label="folder">文件夹上传</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="组件文件" v-if="!isEdit">
|
||||||
|
<el-upload
|
||||||
|
ref="createUploadRef"
|
||||||
|
:auto-upload="false"
|
||||||
|
:multiple="uploadMode === 'files'"
|
||||||
|
:webkitdirectory="uploadMode === 'folder'"
|
||||||
|
:on-change="handleCreateFileChange"
|
||||||
|
:on-remove="handleCreateFileRemove"
|
||||||
|
drag
|
||||||
|
>
|
||||||
|
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||||
|
<div class="el-upload__text">
|
||||||
|
将文件拖到此处,或<em>点击上传</em>
|
||||||
|
</div>
|
||||||
|
<template #tip>
|
||||||
|
<div class="el-upload__tip" v-if="uploadMode === 'files'">
|
||||||
|
可以上传多个文件,每个文件不超过100MB。ZIP文件可以自动解压,其他文件类型仅保存。
|
||||||
|
</div>
|
||||||
|
<div class="el-upload__tip" v-else>
|
||||||
|
可以上传整个文件夹,保持原有目录结构。ZIP文件可以自动解压,每个文件不超过100MB。
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-upload>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
@@ -183,7 +213,6 @@
|
|||||||
ref="uploadRef"
|
ref="uploadRef"
|
||||||
:auto-upload="false"
|
:auto-upload="false"
|
||||||
:limit="1"
|
:limit="1"
|
||||||
accept=".zip"
|
|
||||||
:on-change="handleFileChange"
|
:on-change="handleFileChange"
|
||||||
:on-remove="handleFileRemove"
|
:on-remove="handleFileRemove"
|
||||||
drag
|
drag
|
||||||
@@ -194,7 +223,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<template #tip>
|
<template #tip>
|
||||||
<div class="el-upload__tip">
|
<div class="el-upload__tip">
|
||||||
只能上传zip文件,且不超过100MB
|
文件夹使用压缩为zip格式,可以批量上传文件。
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
@@ -263,8 +292,12 @@ const uploading = ref(false)
|
|||||||
const isEdit = ref(false)
|
const isEdit = ref(false)
|
||||||
const currentComponent = ref(null)
|
const currentComponent = ref(null)
|
||||||
const selectedFile = ref(null)
|
const selectedFile = ref(null)
|
||||||
|
const selectedCreateFile = ref(null)
|
||||||
|
const selectedCreateFiles = ref([])
|
||||||
|
const uploadMode = ref('files') // 'files' 或 'folder'
|
||||||
const formRef = ref(null)
|
const formRef = ref(null)
|
||||||
const uploadRef = ref(null)
|
const uploadRef = ref(null)
|
||||||
|
const createUploadRef = ref(null)
|
||||||
const folderTree = ref([])
|
const folderTree = ref([])
|
||||||
|
|
||||||
// 筛选表单
|
// 筛选表单
|
||||||
@@ -411,9 +444,33 @@ const handleSubmit = async () => {
|
|||||||
await uiComponentApi.updateUIComponent(form.id, form)
|
await uiComponentApi.updateUIComponent(form.id, form)
|
||||||
ElMessage.success('更新成功')
|
ElMessage.success('更新成功')
|
||||||
} else {
|
} else {
|
||||||
|
// 检查是否上传了文件
|
||||||
|
if (selectedCreateFiles.value && selectedCreateFiles.value.length > 0) {
|
||||||
|
// 使用合并接口,同时创建组件和上传文件
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('component_code', form.component_code)
|
||||||
|
formData.append('component_name', form.component_name)
|
||||||
|
formData.append('description', form.description || '')
|
||||||
|
formData.append('version', form.version || '')
|
||||||
|
formData.append('is_active', form.is_active ? 'true' : 'false')
|
||||||
|
formData.append('sort_order', form.sort_order.toString())
|
||||||
|
|
||||||
|
// 添加所有文件
|
||||||
|
selectedCreateFiles.value.forEach(file => {
|
||||||
|
formData.append('files', file.raw)
|
||||||
|
if (uploadMode.value === 'folder') {
|
||||||
|
formData.append('paths', file.path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
await uiComponentApi.createUIComponentWithFile(formData)
|
||||||
|
ElMessage.success('创建并上传成功')
|
||||||
|
} else {
|
||||||
|
// 只创建组件,不上传文件
|
||||||
await uiComponentApi.createUIComponent(form)
|
await uiComponentApi.createUIComponent(form)
|
||||||
ElMessage.success('创建成功')
|
ElMessage.success('创建成功')
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
dialogVisible.value = false
|
dialogVisible.value = false
|
||||||
fetchComponentList()
|
fetchComponentList()
|
||||||
@@ -441,9 +498,15 @@ const resetForm = () => {
|
|||||||
is_active: true,
|
is_active: true,
|
||||||
sort_order: 0
|
sort_order: 0
|
||||||
})
|
})
|
||||||
|
selectedCreateFile.value = null
|
||||||
|
selectedCreateFiles.value = []
|
||||||
|
uploadMode.value = 'files' // 重置为文件上传模式
|
||||||
if (formRef.value) {
|
if (formRef.value) {
|
||||||
formRef.value.resetFields()
|
formRef.value.resetFields()
|
||||||
}
|
}
|
||||||
|
if (createUploadRef.value) {
|
||||||
|
createUploadRef.value.clearFiles()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleUpload = (row) => {
|
const handleUpload = (row) => {
|
||||||
@@ -472,6 +535,20 @@ const handleFileRemove = () => {
|
|||||||
selectedFile.value = null
|
selectedFile.value = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleCreateFileChange = (file, fileList) => {
|
||||||
|
selectedCreateFiles.value = fileList.map(f => ({
|
||||||
|
raw: f.raw,
|
||||||
|
name: f.name,
|
||||||
|
path: uploadMode.value === 'folder' ? (f.raw.webkitRelativePath || f.name) : f.name
|
||||||
|
}))
|
||||||
|
selectedCreateFile.value = fileList.length > 0 ? fileList[0].raw : null // 保留兼容性
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreateFileRemove = () => {
|
||||||
|
selectedCreateFiles.value = []
|
||||||
|
selectedCreateFile.value = null // 保留兼容性
|
||||||
|
}
|
||||||
|
|
||||||
const handleFileSubmit = async () => {
|
const handleFileSubmit = async () => {
|
||||||
if (!selectedFile.value) {
|
if (!selectedFile.value) {
|
||||||
ElMessage.warning('请选择要上传的文件')
|
ElMessage.warning('请选择要上传的文件')
|
||||||
@@ -512,7 +589,17 @@ const handleDownload = async (row) => {
|
|||||||
const url = window.URL.createObjectURL(new Blob([response.data]))
|
const url = window.URL.createObjectURL(new Blob([response.data]))
|
||||||
const link = document.createElement('a')
|
const link = document.createElement('a')
|
||||||
link.href = url
|
link.href = url
|
||||||
link.setAttribute('download', `${row.component_name}.zip`)
|
|
||||||
|
// 根据文件类型确定下载文件名
|
||||||
|
let fileName = row.component_name
|
||||||
|
if (row.file_path) {
|
||||||
|
const fileExtension = row.file_path.substring(row.file_path.lastIndexOf('.'))
|
||||||
|
fileName += fileExtension
|
||||||
|
} else {
|
||||||
|
fileName += '.zip' // 默认扩展名
|
||||||
|
}
|
||||||
|
|
||||||
|
link.setAttribute('download', fileName)
|
||||||
document.body.appendChild(link)
|
document.body.appendChild(link)
|
||||||
link.click()
|
link.click()
|
||||||
document.body.removeChild(link)
|
document.body.removeChild(link)
|
||||||
@@ -620,6 +707,18 @@ const formatDateTime = (dateTime) => {
|
|||||||
return date.toLocaleString('zh-CN')
|
return date.toLocaleString('zh-CN')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 判断文件是否为ZIP类型
|
||||||
|
const isZipFile = (file) => {
|
||||||
|
if (!file) return false
|
||||||
|
return file.toLowerCase().endsWith('.zip')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 判断文件路径是否为ZIP类型
|
||||||
|
const isZipFileFromPath = (path) => {
|
||||||
|
if (!path) return false
|
||||||
|
return path.toLowerCase().endsWith('.zip')
|
||||||
|
}
|
||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
fetchComponentList()
|
fetchComponentList()
|
||||||
|
|||||||
@@ -550,14 +550,29 @@ const loadAlertSettings = async () => {
|
|||||||
try {
|
try {
|
||||||
// 调用API获取用户的余额预警设置
|
// 调用API获取用户的余额预警设置
|
||||||
const response = await balanceAlertApi.getUserAlertSettings()
|
const response = await balanceAlertApi.getUserAlertSettings()
|
||||||
|
|
||||||
|
// 添加日志追踪:接收到的原始数据
|
||||||
|
console.log('[余额预警] 接收到的原始数据:', JSON.stringify(response))
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
balanceAlertSettings.value = response.data
|
// 转换字段名,确保前端使用camelCase
|
||||||
originalAlertSettings.value = JSON.parse(JSON.stringify(response.data))
|
const transformedData = {
|
||||||
|
enabled: response.data.enabled,
|
||||||
|
threshold: response.data.threshold,
|
||||||
|
alertPhone: response.data.alert_phone || response.data.alertPhone || '' // 支持两种字段名,优先使用alert_phone
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加日志追踪:转换后的数据
|
||||||
|
console.log('[余额预警] 转换后的数据:', JSON.stringify(transformedData))
|
||||||
|
|
||||||
|
balanceAlertSettings.value = transformedData
|
||||||
|
originalAlertSettings.value = JSON.parse(JSON.stringify(transformedData))
|
||||||
} else {
|
} else {
|
||||||
|
console.error('[余额预警] 获取余额预警设置失败:', response.message)
|
||||||
ElMessage.error('获取余额预警设置失败')
|
ElMessage.error('获取余额预警设置失败')
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取余额预警设置失败:', error)
|
console.error('[余额预警] 获取余额预警设置异常:', error)
|
||||||
ElMessage.error('获取余额预警设置失败')
|
ElMessage.error('获取余额预警设置失败')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -565,20 +580,31 @@ const loadAlertSettings = async () => {
|
|||||||
// 处理预警开关变化
|
// 处理预警开关变化
|
||||||
const handleAlertEnabledChange = (value) => {
|
const handleAlertEnabledChange = (value) => {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
// 禁用预警时,可以清空手机号
|
// 禁用预警时,保留手机号不清空,但记录日志
|
||||||
balanceAlertSettings.value.alertPhone = ''
|
console.log('[余额预警] 预警已禁用,保留手机号:', balanceAlertSettings.value.alertPhone)
|
||||||
} else {
|
} else {
|
||||||
// 启用预警时,使用用户手机号作为默认值
|
// 启用预警时,如果用户从未设置过预警手机号,则默认使用用户手机号
|
||||||
if (!balanceAlertSettings.value.alertPhone && userInfo.value?.phone) {
|
// 如果原始数据中alertPhone为空,说明用户从未设置过,此时才设置默认值
|
||||||
|
if (!originalAlertSettings.value.alertPhone && userInfo.value?.phone) {
|
||||||
balanceAlertSettings.value.alertPhone = userInfo.value.phone
|
balanceAlertSettings.value.alertPhone = userInfo.value.phone
|
||||||
|
console.log('[余额预警] 首次启用预警,设置默认手机号:', userInfo.value.phone)
|
||||||
|
} else {
|
||||||
|
console.log('[余额预警] 预警已启用,保持现有手机号:', balanceAlertSettings.value.alertPhone)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理阈值变化
|
// 处理阈值变化
|
||||||
const handleThresholdChange = (value) => {
|
const handleThresholdChange = (value) => {
|
||||||
if (value < 0) {
|
// 确保值是数字类型
|
||||||
|
const numValue = Number(value)
|
||||||
|
|
||||||
|
if (isNaN(numValue) || numValue < 0) {
|
||||||
balanceAlertSettings.value.threshold = 0
|
balanceAlertSettings.value.threshold = 0
|
||||||
|
console.log('[余额预警] 阈值已重置为0,输入值无效:', value)
|
||||||
|
} else {
|
||||||
|
balanceAlertSettings.value.threshold = numValue
|
||||||
|
console.log('[余额预警] 阈值已更新:', numValue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -588,7 +614,12 @@ const handlePhoneChange = (event) => {
|
|||||||
// 简单的手机号验证
|
// 简单的手机号验证
|
||||||
if (phone && !/^1[3-9]\d{9}$/.test(phone)) {
|
if (phone && !/^1[3-9]\d{9}$/.test(phone)) {
|
||||||
ElMessage.warning('请输入正确的手机号格式')
|
ElMessage.warning('请输入正确的手机号格式')
|
||||||
|
console.log('[余额预警] 手机号格式不正确:', phone)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 更新数据模型中的手机号
|
||||||
|
balanceAlertSettings.value.alertPhone = phone
|
||||||
|
console.log('[余额预警] 手机号已更新:', phone)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存余额预警设置
|
// 保存余额预警设置
|
||||||
@@ -600,16 +631,42 @@ const saveAlertSettings = async () => {
|
|||||||
|
|
||||||
alertSettingsLoading.value = true
|
alertSettingsLoading.value = true
|
||||||
try {
|
try {
|
||||||
|
// 准备发送给后端的数据,确保字段名使用后端风格 (snake_case)
|
||||||
|
const dataToSave = {
|
||||||
|
enabled: balanceAlertSettings.value.enabled,
|
||||||
|
threshold: balanceAlertSettings.value.threshold,
|
||||||
|
alert_phone: balanceAlertSettings.value.alertPhone // 转换字段名
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加日志追踪:准备发送的数据
|
||||||
|
console.log('[余额预警] 准备发送的数据:', JSON.stringify(dataToSave))
|
||||||
|
console.log('[余额预警] 当前表单数据:', JSON.stringify(balanceAlertSettings.value))
|
||||||
|
|
||||||
// 调用API保存用户的余额预警设置
|
// 调用API保存用户的余额预警设置
|
||||||
const response = await balanceAlertApi.updateUserAlertSettings(balanceAlertSettings.value)
|
const response = await balanceAlertApi.updateUserAlertSettings(dataToSave)
|
||||||
|
|
||||||
|
// 添加日志追踪:API响应
|
||||||
|
console.log('[余额预警] API响应:', JSON.stringify(response))
|
||||||
|
|
||||||
if (response.success) {
|
if (response.success) {
|
||||||
originalAlertSettings.value = JSON.parse(JSON.stringify(balanceAlertSettings.value))
|
// 确保数据结构一致,只保留前端使用的字段
|
||||||
|
const updatedData = {
|
||||||
|
enabled: balanceAlertSettings.value.enabled,
|
||||||
|
threshold: balanceAlertSettings.value.threshold,
|
||||||
|
alertPhone: balanceAlertSettings.value.alertPhone // 确保使用前端字段名
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加日志追踪:更新后的数据
|
||||||
|
console.log('[余额预警] 更新后的数据:', JSON.stringify(updatedData))
|
||||||
|
|
||||||
|
originalAlertSettings.value = JSON.parse(JSON.stringify(updatedData))
|
||||||
ElMessage.success('余额预警设置保存成功')
|
ElMessage.success('余额预警设置保存成功')
|
||||||
} else {
|
} else {
|
||||||
|
console.error('[余额预警] 保存失败:', response.message)
|
||||||
ElMessage.error('保存失败:' + response.message)
|
ElMessage.error('保存失败:' + response.message)
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存余额预警设置失败:', error)
|
console.error('[余额预警] 保存余额预警设置失败:', error)
|
||||||
ElMessage.error('保存余额预警设置失败')
|
ElMessage.error('保存余额预警设置失败')
|
||||||
} finally {
|
} finally {
|
||||||
alertSettingsLoading.value = false
|
alertSettingsLoading.value = false
|
||||||
|
|||||||
@@ -286,6 +286,12 @@ const routes = [
|
|||||||
name: 'AdminStatistics',
|
name: 'AdminStatistics',
|
||||||
component: () => import('@/pages/admin/statistics/SystemStatisticsPage.vue'),
|
component: () => import('@/pages/admin/statistics/SystemStatisticsPage.vue'),
|
||||||
meta: { title: '系统统计' }
|
meta: { title: '系统统计' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'ui-components',
|
||||||
|
name: 'AdminUIComponents',
|
||||||
|
component: () => import('@/pages/admin/ui-components/index.vue'),
|
||||||
|
meta: { title: '组件管理' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user