This commit is contained in:
menxipeng
2025-10-21 08:03:33 +08:00
parent 971935bae2
commit e77d28abf7
4 changed files with 888 additions and 14 deletions

View File

@@ -9,6 +9,7 @@ import UsersPage from './components/pages/UsersPage'
import EquipmentPage from './components/pages/EquipmentPage'
import StatisticsPage from './components/pages/StatisticsPage'
import MallsPage from './components/pages/MallsPage'
import VersionPage from './components/pages/VersionPage'
import DynamicPage from './components/DynamicPage'
function App() {
@@ -25,6 +26,7 @@ function App() {
<Route path="users" element={<UsersPage />} />
<Route path="equipment" element={<EquipmentPage />} />
<Route path="malls" element={<MallsPage />} />
<Route path="version" element={<VersionPage />} />
<Route path=":slug" element={<DynamicPage />} />
<Route path=":slug/*" element={<DynamicPage />} />
</Route>

View File

@@ -15,7 +15,7 @@ import {
DialogTitle,
DialogTrigger,
} from "../ui/dialog"
import { Plus, Search, Filter, Download, Calendar, AlertTriangle, Store, Eye, Building2, MapPin } from "lucide-react"
import { Plus, Search, Filter, Download, Calendar, AlertTriangle, Store, Eye, Building2, MapPin, ChevronLeft, ChevronRight } from "lucide-react"
import { apiGet, apiPost, apiPut } from "../../lib/services/api"
// 总商户数据类型
@@ -77,6 +77,8 @@ export default function MerchantsPage() {
const [merchantEquipments, setMerchantEquipments] = useState<{[key: string]: any[]}>({})
const [loadingMerchantEquipments, setLoadingMerchantEquipments] = useState<{[key: string]: boolean}>({})
const [userRole, setUserRole] = useState<string>("")
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(10)
const [newMerchant, setNewMerchant] = useState({
name: "",
@@ -213,10 +215,10 @@ export default function MerchantsPage() {
}
// 获取商户列表
const fetchMerchants = async () => {
const fetchMerchants = async (page = currentPage, size = pageSize) => {
setLoadingMerchants(true)
try {
const response = await apiGet('/back/merchants/list')
const response = await apiGet(`/back/merchants/list?pageNum=${page}&pageSize=${size}`)
if (response.code === 200) {
const merchantsData = response.rows || []
setTotal(parseInt(response.total) || 0)
@@ -245,6 +247,19 @@ export default function MerchantsPage() {
}
}
// 页码变化处理
const handlePageChange = (page: number) => {
setCurrentPage(page)
fetchMerchants(page, pageSize)
}
// 每页大小变化处理
const handlePageSizeChange = (size: number) => {
setPageSize(size)
setCurrentPage(1)
fetchMerchants(1, size)
}
// 组件加载时获取数据
useEffect(() => {
fetchUserRole()
@@ -323,7 +338,7 @@ export default function MerchantsPage() {
setIsAddMerchantOpen(false)
// 刷新商户列表
fetchMerchants()
fetchMerchants(currentPage, pageSize)
} else {
console.error('添加商户失败:', result)
alert('添加商户失败:' + (result.msg || '未知错误'))
@@ -460,7 +475,7 @@ export default function MerchantsPage() {
console.log('编辑商户成功:', result)
alert('编辑商户成功!')
setIsEditMerchantOpen(false)
fetchMerchants()
fetchMerchants(currentPage, pageSize)
} else {
console.error('编辑商户失败:', result)
alert('编辑商户失败:' + (result.msg || '未知错误'))
@@ -576,7 +591,7 @@ export default function MerchantsPage() {
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
<CardDescription> - {total} {currentPage} </CardDescription>
</CardHeader>
<CardContent>
<div className="flex flex-col sm:flex-row gap-4 mb-6">
@@ -705,6 +720,56 @@ export default function MerchantsPage() {
</TableBody>
</Table>
</div>
{/* Pagination */}
<div className="flex items-center justify-between space-x-2 py-4">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium"></p>
<Select
value={pageSize.toString()}
onValueChange={(value) => handlePageSizeChange(parseInt(value))}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue />
</SelectTrigger>
<SelectContent side="top">
<SelectItem value="5">5</SelectItem>
<SelectItem value="10">10</SelectItem>
<SelectItem value="20">20</SelectItem>
<SelectItem value="50">50</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
{total}
</p>
</div>
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">
{currentPage} {Math.ceil(total / pageSize)}
</p>
<div className="flex items-center space-x-1">
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage <= 1 || loadingMerchants}
>
<ChevronLeft className="h-4 w-4" />
<span className="sr-only"></span>
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage >= Math.ceil(total / pageSize) || loadingMerchants}
>
<ChevronRight className="h-4 w-4" />
<span className="sr-only"></span>
</Button>
</div>
</div>
</div>
</CardContent>
</Card>

View File

@@ -0,0 +1,803 @@
import React, { useState, useEffect } from "react"
import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"
import { Button } from "../ui/button"
import { Input } from "../ui/input"
import { Badge } from "../ui/badge"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "../ui/table"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "../ui/select"
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "../ui/dialog"
import { Label } from "../ui/label"
import { Textarea } from "../ui/textarea"
import {
Plus,
Search,
Download,
Edit,
Trash2,
Upload,
RefreshCw,
ChevronLeft,
ChevronRight,
} from "lucide-react"
import { apiGet, apiPost, apiPut, apiDelete } from "../../lib/services/api"
import { getUserToken } from "../../lib/utils/storage"
// 版本信息数据类型接口
interface Version {
id: string
versionCode: number
versionName: string
isForce: string
channel: string
downloadUrl: string
updateLog: string
size: string
createTime: string
updateTime: string
}
// 渠道中文名称映射
const channelNameMap: Record<string, string> = {
'official': '官方',
'xiaomi': '小米',
'huawei': '华为',
'oppo': 'OPPO',
'vivo': 'VIVO',
'meizu': '魅族',
'honor': '荣耀',
'yingyongbao': '应用宝',
'ios': 'iOS'
}
export default function VersionPage() {
const [versions, setVersions] = useState<Version[]>([])
const [channels, setChannels] = useState<string[]>([])
const [searchTerm, setSearchTerm] = useState("")
const [isForceFilter, setIsForceFilter] = useState("all")
const [channelFilter, setChannelFilter] = useState("all")
const [isAddDialogOpen, setIsAddDialogOpen] = useState(false)
const [isEditDialogOpen, setIsEditDialogOpen] = useState(false)
const [loading, setLoading] = useState(false)
const [submitting, setSubmitting] = useState(false)
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(10)
const [total, setTotal] = useState(0)
const [selectedFile, setSelectedFile] = useState<File | null>(null)
const [uploadProgress, setUploadProgress] = useState(0)
const [formData, setFormData] = useState({
versionName: "",
versionCode: 4,
isForce: "0",
channel: "",
downloadUrl: "",
updateLog: "",
size: "",
})
const [editData, setEditData] = useState<Version | null>(null)
// 获取版本列表
const fetchVersions = async (page = currentPage, size = pageSize) => {
setLoading(true)
try {
const params: any = {
pageNum: page,
pageSize: size,
}
if (searchTerm) params.versionName = searchTerm
if (isForceFilter !== "all") params.isForce = isForceFilter
if (channelFilter !== "all") params.channel = channelFilter
const response = await apiGet('/back/version/list', params)
if (response.code === 200) {
setVersions(response.rows || [])
setTotal(parseInt(response.total) || 0)
}
} catch (error) {
console.error('获取版本列表失败:', error)
} finally {
setLoading(false)
}
}
// 获取渠道列表
const fetchChannels = async () => {
try {
const response = await apiGet('/back/version/getChannel')
if (response.code === 200) {
setChannels(response.data || [])
}
} catch (error) {
console.error('获取渠道列表失败:', error)
}
}
// 组件加载时获取数据
useEffect(() => {
fetchVersions()
fetchChannels()
}, [])
// 分页变化时重新获取数据
useEffect(() => {
fetchVersions(currentPage, pageSize)
}, [currentPage, pageSize])
// 处理文件选择
const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
const file = e.target.files?.[0]
if (!file) {
console.log('没有选择文件')
return
}
console.log('选择的文件:', file.name, '大小:', file.size)
// 检查文件大小500MB限制
const maxSize = 500 * 1024 * 1024
if (file.size > maxSize) {
alert('上传文件大小不能超过 500MB!')
return
}
setSelectedFile(file)
// 计算文件大小
const fileSize = file.size
let sizeText = ''
if (fileSize < 1024) {
sizeText = fileSize + ' B'
} else if (fileSize < 1024 * 1024) {
sizeText = (fileSize / 1024).toFixed(2) + ' KB'
} else if (fileSize < 1024 * 1024 * 1024) {
sizeText = (fileSize / (1024 * 1024)).toFixed(2) + ' MB'
} else {
sizeText = (fileSize / (1024 * 1024 * 1024)).toFixed(2) + ' GB'
}
console.log('文件大小:', sizeText)
if (isEditDialogOpen && editData) {
setEditData({ ...editData, size: sizeText })
} else {
setFormData({ ...formData, size: sizeText })
}
// 上传文件
console.log('开始上传文件...')
await uploadFile(file)
}
// 上传文件到服务器
const uploadFile = async (file: File) => {
console.log('uploadFile 函数被调用')
const formDataUpload = new FormData()
formDataUpload.append('file', file)
console.log('准备上传的文件:', file.name)
try {
const token = getUserToken()
console.log('Token:', token ? '存在' : '不存在')
// 使用当前页面的域名和端口,自动适配开发和生产环境
const baseUrl = window.location.origin.includes('localhost')
? 'http://localhost:8080/api'
: '/api'
const url = `${baseUrl}/back/version/upload`
console.log('上传URL:', url)
const response = await fetch(url, {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`
},
body: formDataUpload
})
console.log('响应状态:', response.status)
const result = await response.json()
console.log('响应结果:', result)
if (result.code === 200) {
if (isEditDialogOpen && editData) {
setEditData({ ...editData, downloadUrl: result.data })
} else {
setFormData({ ...formData, downloadUrl: result.data })
}
alert('文件上传成功')
} else {
alert('文件上传失败:' + result.msg)
}
} catch (error) {
console.error('文件上传失败:', error)
alert('文件上传失败: ' + (error instanceof Error ? error.message : '未知错误'))
}
}
// 添加版本
const handleAddVersion = async () => {
if (!formData.versionName || !formData.versionCode || !formData.channel || !formData.downloadUrl) {
alert('请填写所有必填字段')
return
}
setSubmitting(true)
try {
const response = await apiPost('/back/version', {
...formData,
versionCode: Number(formData.versionCode)
})
if (response.code === 200) {
alert('新增成功')
setIsAddDialogOpen(false)
resetForm()
fetchVersions(currentPage, pageSize)
} else {
alert('新增失败:' + response.msg)
}
} catch (error) {
console.error('新增版本失败:', error)
alert('新增失败,请稍后重试')
} finally {
setSubmitting(false)
}
}
// 打开编辑对话框
const handleEditVersion = async (version: Version) => {
setEditData(version)
setIsEditDialogOpen(true)
}
// 更新版本
const handleUpdateVersion = async () => {
if (!editData) return
if (!editData.versionName || !editData.versionCode || !editData.channel || !editData.downloadUrl) {
alert('请填写所有必填字段')
return
}
setSubmitting(true)
try {
const response = await apiPut('/back/version', {
...editData,
versionCode: Number(editData.versionCode)
})
if (response.code === 200) {
alert('修改成功')
setIsEditDialogOpen(false)
setEditData(null)
fetchVersions(currentPage, pageSize)
} else {
alert('修改失败:' + response.msg)
}
} catch (error) {
console.error('修改版本失败:', error)
alert('修改失败,请稍后重试')
} finally {
setSubmitting(false)
}
}
// 删除版本
const handleDeleteVersion = async (id: string) => {
if (!window.confirm('是否确认删除该版本信息?')) return
try {
const response = await apiDelete(`/back/version/${id}`)
if (response.code === 200) {
alert('删除成功')
fetchVersions(currentPage, pageSize)
} else {
alert('删除失败:' + response.msg)
}
} catch (error) {
console.error('删除版本失败:', error)
alert('删除失败,请稍后重试')
}
}
// 重置表单
const resetForm = () => {
setFormData({
versionName: "",
versionCode: 4,
isForce: "0",
channel: "",
downloadUrl: "",
updateLog: "",
size: "",
})
setSelectedFile(null)
}
// 搜索
const handleSearch = () => {
setCurrentPage(1)
fetchVersions(1, pageSize)
}
// 重置搜索
const handleReset = () => {
setSearchTerm("")
setIsForceFilter("all")
setChannelFilter("all")
setCurrentPage(1)
fetchVersions(1, pageSize)
}
// 导出
const handleExport = () => {
// 实现导出功能
alert('导出功能开发中')
}
// 分页处理
const handlePageChange = (page: number) => {
setCurrentPage(page)
}
const handlePageSizeChange = (size: number) => {
setPageSize(size)
setCurrentPage(1)
}
return (
<div className="space-y-6">
{/* 页面标题 */}
<div>
<h1 className="text-3xl font-bold text-gray-900"></h1>
<p className="text-gray-600"></p>
</div>
{/* 搜索表单 */}
<Card>
<CardContent className="pt-6">
<div className="grid grid-cols-1 md:grid-cols-4 gap-4 mb-4">
<div>
<Label></Label>
<Input
placeholder="请输入版本名称"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onKeyPress={(e) => e.key === 'Enter' && handleSearch()}
/>
</div>
<div>
<Label></Label>
<Select value={isForceFilter} onValueChange={setIsForceFilter}>
<SelectTrigger>
<SelectValue placeholder="请选择" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
<SelectItem value="0"></SelectItem>
<SelectItem value="1"></SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label></Label>
<Select value={channelFilter} onValueChange={setChannelFilter}>
<SelectTrigger>
<SelectValue placeholder="请选择" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all"></SelectItem>
{channels.map((channel) => (
<SelectItem key={channel} value={channel.toLowerCase()}>
{channelNameMap[channel.toLowerCase()] || channel.toLowerCase()}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-end gap-2">
<Button onClick={handleSearch}>
<Search className="h-4 w-4 mr-2" />
</Button>
<Button variant="outline" onClick={handleReset}>
<RefreshCw className="h-4 w-4 mr-2" />
</Button>
</div>
</div>
</CardContent>
</Card>
{/* 操作按钮 */}
<div className="flex gap-2">
<Button onClick={() => setIsAddDialogOpen(true)}>
<Plus className="h-4 w-4 mr-2" />
</Button>
<Button variant="outline" onClick={handleExport}>
<Download className="h-4 w-4 mr-2" />
</Button>
</div>
{/* 版本列表 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="rounded-md border">
<Table>
<TableHeader>
<TableRow>
<TableHead>ID</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead>APK下载</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{loading ? (
<TableRow>
<TableCell colSpan={9} className="text-center py-8">
<div className="text-gray-500">...</div>
</TableCell>
</TableRow>
) : versions.length === 0 ? (
<TableRow>
<TableCell colSpan={9} className="text-center py-8">
<div className="text-gray-500"></div>
</TableCell>
</TableRow>
) : (
versions.map((version) => (
<TableRow key={version.id}>
<TableCell>{version.id}</TableCell>
<TableCell>{version.versionCode}</TableCell>
<TableCell>{version.versionName}</TableCell>
<TableCell>
<Badge className={version.isForce === '1' ? "bg-red-100 text-red-800" : "bg-green-100 text-green-800"}>
{version.isForce === '1' ? '是' : '否'}
</Badge>
</TableCell>
<TableCell>
<Badge variant="outline">
{channelNameMap[version.channel] || version.channel}
</Badge>
</TableCell>
<TableCell>
<div className="max-w-xs truncate" title={version.downloadUrl}>
{version.downloadUrl}
</div>
</TableCell>
<TableCell>
<div className="max-w-xs truncate" title={version.updateLog}>
{version.updateLog || '-'}
</div>
</TableCell>
<TableCell>{version.size}</TableCell>
<TableCell>
<div className="flex gap-2">
<Button
variant="ghost"
size="sm"
onClick={() => handleEditVersion(version)}
>
<Edit className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="sm"
onClick={() => handleDeleteVersion(version.id)}
>
<Trash2 className="h-4 w-4 text-red-600" />
</Button>
</div>
</TableCell>
</TableRow>
))
)}
</TableBody>
</Table>
</div>
{/* 分页 */}
<div className="flex items-center justify-between space-x-2 py-4">
<div className="flex items-center space-x-2">
<p className="text-sm font-medium"></p>
<Select
value={pageSize.toString()}
onValueChange={(value) => handlePageSizeChange(parseInt(value))}
>
<SelectTrigger className="h-8 w-[70px]">
<SelectValue />
</SelectTrigger>
<SelectContent side="top">
<SelectItem value="10">10</SelectItem>
<SelectItem value="20">20</SelectItem>
<SelectItem value="50">50</SelectItem>
</SelectContent>
</Select>
<p className="text-sm text-muted-foreground">
{total}
</p>
</div>
<div className="flex items-center space-x-2">
<p className="text-sm font-medium">
{currentPage} {Math.ceil(total / pageSize)}
</p>
<div className="flex items-center space-x-1">
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(currentPage - 1)}
disabled={currentPage <= 1 || loading}
>
<ChevronLeft className="h-4 w-4" />
</Button>
<Button
variant="outline"
size="sm"
onClick={() => handlePageChange(currentPage + 1)}
disabled={currentPage >= Math.ceil(total / pageSize) || loading}
>
<ChevronRight className="h-4 w-4" />
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
{/* 新增对话框 */}
<Dialog open={isAddDialogOpen} onOpenChange={setIsAddDialogOpen}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription>APK文件</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="versionName"> <span className="text-red-500">*</span></Label>
<Input
id="versionName"
value={formData.versionName}
onChange={(e) => setFormData({ ...formData, versionName: e.target.value })}
placeholder="请输入版本名称"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="versionCode"> <span className="text-red-500">*</span></Label>
<Input
id="versionCode"
type="number"
min="4"
value={formData.versionCode}
onChange={(e) => setFormData({ ...formData, versionCode: parseInt(e.target.value) || 4 })}
placeholder="请输入版本代码"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="isForce"> <span className="text-red-500">*</span></Label>
<Select value={formData.isForce} onValueChange={(value) => setFormData({ ...formData, isForce: value })}>
<SelectTrigger>
<SelectValue placeholder="请选择" />
</SelectTrigger>
<SelectContent>
<SelectItem value="0"></SelectItem>
<SelectItem value="1"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="channel"> <span className="text-red-500">*</span></Label>
<Select value={formData.channel} onValueChange={(value) => setFormData({ ...formData, channel: value })}>
<SelectTrigger>
<SelectValue placeholder="请选择渠道" />
</SelectTrigger>
<SelectContent>
{channels.map((channel) => (
<SelectItem key={channel} value={channel.toLowerCase()}>
{channelNameMap[channel.toLowerCase()] || channel.toLowerCase()}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="file">APK下载 <span className="text-red-500">*</span></Label>
<div className="flex items-center gap-2">
<Input
id="file"
type="file"
accept=".apk"
onChange={handleFileChange}
className="hidden"
/>
<Button
type="button"
variant="outline"
onClick={() => document.getElementById('file')?.click()}
>
<Upload className="h-4 w-4 mr-2" />
</Button>
<span className="text-sm text-gray-500">500MB</span>
</div>
<Input
value={formData.downloadUrl}
placeholder="文件上传后自动填入地址"
readOnly
className="mt-2 bg-gray-50"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="updateLog"></Label>
<Textarea
id="updateLog"
value={formData.updateLog}
onChange={(e) => setFormData({ ...formData, updateLog: e.target.value })}
placeholder="请输入更新日志"
rows={3}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="size"></Label>
<Input
id="size"
value={formData.size}
onChange={(e) => setFormData({ ...formData, size: e.target.value })}
placeholder="上传文件后自动填入"
readOnly
className="bg-gray-50"
/>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => { setIsAddDialogOpen(false); resetForm(); }}>
</Button>
<Button onClick={handleAddVersion} disabled={submitting}>
{submitting ? "提交中..." : "确定"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
{/* 编辑对话框 */}
<Dialog open={isEditDialogOpen} onOpenChange={setIsEditDialogOpen}>
<DialogContent className="max-w-2xl">
<DialogHeader>
<DialogTitle></DialogTitle>
<DialogDescription></DialogDescription>
</DialogHeader>
{editData && (
<div className="grid gap-4 py-4">
<div className="grid gap-2">
<Label htmlFor="edit-versionName"> <span className="text-red-500">*</span></Label>
<Input
id="edit-versionName"
value={editData.versionName}
onChange={(e) => setEditData({ ...editData, versionName: e.target.value })}
placeholder="请输入版本名称"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-versionCode"> <span className="text-red-500">*</span></Label>
<Input
id="edit-versionCode"
type="number"
min="4"
value={editData.versionCode}
onChange={(e) => setEditData({ ...editData, versionCode: parseInt(e.target.value) || 4 })}
placeholder="请输入版本代码"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-isForce"> <span className="text-red-500">*</span></Label>
<Select value={editData.isForce} onValueChange={(value) => setEditData({ ...editData, isForce: value })}>
<SelectTrigger>
<SelectValue placeholder="请选择" />
</SelectTrigger>
<SelectContent>
<SelectItem value="0"></SelectItem>
<SelectItem value="1"></SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-channel"> <span className="text-red-500">*</span></Label>
<Select value={editData.channel} onValueChange={(value) => setEditData({ ...editData, channel: value })}>
<SelectTrigger>
<SelectValue placeholder="请选择渠道" />
</SelectTrigger>
<SelectContent>
{channels.map((channel) => (
<SelectItem key={channel} value={channel.toLowerCase()}>
{channelNameMap[channel.toLowerCase()] || channel.toLowerCase()}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-file">APK下载 <span className="text-red-500">*</span></Label>
<div className="flex items-center gap-2">
<Input
id="edit-file"
type="file"
accept=".apk"
onChange={handleFileChange}
className="hidden"
/>
<Button
type="button"
variant="outline"
onClick={() => document.getElementById('edit-file')?.click()}
>
<Upload className="h-4 w-4 mr-2" />
</Button>
<span className="text-sm text-gray-500">500MB</span>
</div>
<Input
value={editData.downloadUrl}
placeholder="文件上传后自动填入地址"
readOnly
className="mt-2 bg-gray-50"
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-updateLog"></Label>
<Textarea
id="edit-updateLog"
value={editData.updateLog}
onChange={(e) => setEditData({ ...editData, updateLog: e.target.value })}
placeholder="请输入更新日志"
rows={3}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="edit-size"></Label>
<Input
id="edit-size"
value={editData.size}
onChange={(e) => setEditData({ ...editData, size: e.target.value })}
placeholder="上传文件后自动填入"
readOnly
className="bg-gray-50"
/>
</div>
</div>
)}
<DialogFooter>
<Button variant="outline" onClick={() => { setIsEditDialogOpen(false); setEditData(null); }}>
</Button>
<Button onClick={handleUpdateVersion} disabled={submitting}>
{submitting ? "提交中..." : "确定"}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
)
}

View File

@@ -594,12 +594,15 @@ function CreateWorkOrderForm({ onClose }: { onClose: () => void }) {
}
// 获取商户设备列表
const fetchMerchantEquipments = async (merchantsId: string) => {
const fetchMerchantEquipments = async (merchantId: string) => {
setLoadingEquipment(true)
console.log('获取商户设备商户ID:', merchantId)
try {
const response = await apiGet(`/back/equipment/list?pageNum=1&pageSize=9999&merchantsId=${merchantsId}`)
const response = await apiGet(`/back/equipment/list?pageNum=1&pageSize=9999&merchantId=${merchantId}`)
console.log('设备列表响应:', response)
if (response.code === 200) {
setAvailableEquipment(response.rows || [])
console.log('设备列表:', response.rows)
}
} catch (error) {
console.error('获取设备列表失败:', error)
@@ -626,16 +629,17 @@ function CreateWorkOrderForm({ onClose }: { onClose: () => void }) {
}
const handleMerchantChange = (merchantId: string) => {
console.log('选择商户ID:', merchantId)
setSelectedMerchant(merchantId)
setFormData({ ...formData, merchantId, equipmentId: "" })
// 找到选中的商户对象,获取merchantsId
// 找到选中的商户对象,使用 merchantsId 来获取设备列表
const selectedMerchantObj = merchants.find(m => m.id === merchantId)
if (selectedMerchantObj && selectedMerchantObj.merchantsId) {
fetchMerchantEquipments(selectedMerchantObj.merchantsId)
} else {
// 如果没有merchantsId使用id作为fallback
fetchMerchantEquipments(merchantId)
if (selectedMerchantObj) {
// 使用 merchantsId 来查询设备,如果没有则使用 id
const merchantIdForEquipment = selectedMerchantObj.merchantsId || selectedMerchantObj.id
console.log('用于查询设备的 merchantId:', merchantIdForEquipment)
fetchMerchantEquipments(merchantIdForEquipment)
}
}