产品列表 水滴静态-游戏/ 套餐/线路列表
This commit is contained in:
@@ -116,6 +116,11 @@ export default function ChannelsPage() {
|
||||
const [page, setPage] = useState(1)
|
||||
const [selection, setSelection] = useState<Record<string, boolean>>({})
|
||||
|
||||
const selectedAccounts = useMemo(
|
||||
() => data.filter(item => selection[String(item.Id)]),
|
||||
[data, selection],
|
||||
)
|
||||
|
||||
const selectedProduct = useMemo(
|
||||
() =>
|
||||
products.find(item => String(item.Product.Id) === query.productId) ??
|
||||
@@ -207,7 +212,10 @@ export default function ChannelsPage() {
|
||||
<SelectContent>
|
||||
<SelectItem value="0">全部产品</SelectItem>
|
||||
{products.map(item => (
|
||||
<SelectItem key={item.Product.Id} value={String(item.Product.Id)}>
|
||||
<SelectItem
|
||||
key={item.Product.Id}
|
||||
value={String(item.Product.Id)}
|
||||
>
|
||||
{item.Product.Name}
|
||||
</SelectItem>
|
||||
))}
|
||||
@@ -256,7 +264,25 @@ export default function ChannelsPage() {
|
||||
<div className="flex flex-wrap items-center gap-4 pt-2 p-4">
|
||||
<Button
|
||||
className="bg-red-600 hover:bg-red-700 text-white"
|
||||
onClick={() => toast.info("续费功能开发中,敬请期待")}
|
||||
onClick={() => {
|
||||
if (selectedAccounts.length === 0) {
|
||||
toast.error("请先勾选需要续费的账号")
|
||||
return
|
||||
}
|
||||
const productIds = new Set(
|
||||
selectedAccounts.map(item => item.ProductId),
|
||||
)
|
||||
if (productIds.size > 1) {
|
||||
toast.error("所选账号必须为同一种产品")
|
||||
return
|
||||
}
|
||||
const accounts = selectedAccounts
|
||||
.map(item => item.Account)
|
||||
.join(",")
|
||||
const productId = selectedAccounts[0].ProductId
|
||||
// 续费走后端老页面(/product/rebuyindex → /product/rebuy),与 juipnet 行为一致
|
||||
window.location.href = `/product/rebuyindex?accounts=${encodeURIComponent(accounts)}&productId=${productId}`
|
||||
}}
|
||||
>
|
||||
续费
|
||||
</Button>
|
||||
|
||||
84
src/api/shuidi.ts
Normal file
84
src/api/shuidi.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import type { ApiResponse } from "@/lib/api"
|
||||
import type {
|
||||
ShuidiGame,
|
||||
ShuidiPackageList,
|
||||
ShuidiRegion,
|
||||
} from "@/lib/models/shuidi"
|
||||
import { phpFetch, phpGet, phpPost } from "./http"
|
||||
|
||||
// ==================== 水滴静态/固态(jinyou) ====================
|
||||
// - 游戏列表:POST /jinyou/api/gameList(不带 cookie)
|
||||
// - 套餐列表:POST /jinyou/Jinyoujt/getPackage?type=1(1:静态 2:固态),需带 cookie
|
||||
// - 线路列表:GET /jinyou/api/regionSurplusOuts?product_id=&is_unshar=&game_id=(不带 cookie)
|
||||
|
||||
type JinyouResult<T> = {
|
||||
code: number
|
||||
msg?: string
|
||||
data?: T
|
||||
}
|
||||
|
||||
export async function fetchShuidiGameList(): Promise<
|
||||
ApiResponse<ShuidiGame[]>
|
||||
> {
|
||||
const res = await phpFetch<JinyouResult<ShuidiGame[]>>(
|
||||
"POST",
|
||||
"/jinyou/api/gameList",
|
||||
)
|
||||
if (!res.success) return res
|
||||
const data = res.data?.data
|
||||
return {
|
||||
success: true,
|
||||
data: Array.isArray(data) ? data : [],
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchShuidiPackages(
|
||||
type: 1 | 2 = 1,
|
||||
): Promise<ApiResponse<ShuidiPackageList>> {
|
||||
const res = await phpPost<JinyouResult<ShuidiPackageList>>(
|
||||
`/jinyou/Jinyoujt/getPackage?type=${type}`,
|
||||
{ cookie: document.cookie },
|
||||
)
|
||||
if (!res.success) return res
|
||||
const data = res.data?.data
|
||||
if (!data || typeof data !== "object") {
|
||||
return { success: true, data: { game: {}, line: {} } }
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
game: data.game ?? {},
|
||||
line: data.line ?? {},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchShuidiRegions(params: {
|
||||
product_id: number
|
||||
is_unshar: 0 | 1
|
||||
game_id: number | ""
|
||||
}): Promise<ApiResponse<ShuidiRegion>> {
|
||||
const query = new URLSearchParams({
|
||||
product_id: String(params.product_id),
|
||||
is_unshar: String(params.is_unshar),
|
||||
game_id: String(params.game_id),
|
||||
})
|
||||
const res = await phpGet<JinyouResult<{ region?: ShuidiRegion }>>(
|
||||
`/jinyou/api/regionSurplusOuts?${query.toString()}`,
|
||||
)
|
||||
if (!res.success) return res
|
||||
const region = res.data?.data?.region
|
||||
if (!region || typeof region !== "object") {
|
||||
return {
|
||||
success: true,
|
||||
data: { province_surplus_outs: {} },
|
||||
}
|
||||
}
|
||||
return {
|
||||
success: true,
|
||||
data: {
|
||||
surplus_out_total: region.surplus_out_total,
|
||||
province_surplus_outs: region.province_surplus_outs ?? {},
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -247,6 +247,8 @@ export default function BuyPage() {
|
||||
PackageId: packageId,
|
||||
Pwd: pwd.trim(),
|
||||
ConnectCount: displayConnectCount,
|
||||
OrderType: 1,
|
||||
Price: product.price,
|
||||
CouponAmount: 0,
|
||||
CouponId: 0,
|
||||
OPayType: oPayType,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,10 @@ export type CreateOrderRequest = {
|
||||
PackageId: number
|
||||
Pwd: string
|
||||
ConnectCount: number
|
||||
// 订单类型:1新开 2批量新开 3续费 4批量续费 5退款
|
||||
OrderType: number
|
||||
// 套餐单价(后端按 单价×连接数×账号数 计算总价)
|
||||
Price: number
|
||||
// 优惠券减免金额
|
||||
CouponAmount: number
|
||||
// 优惠券ID
|
||||
@@ -133,11 +137,13 @@ export const DEFAULT_FILTERS: OrderFilters = {
|
||||
eTime: "",
|
||||
}
|
||||
|
||||
// 与后端 Hncore.Pass.Vpn.Domain.OrderType 枚举保持一致
|
||||
export const ORDER_TYPE_LABELS: Record<number, string> = {
|
||||
1: "新开",
|
||||
2: "续费",
|
||||
3: "批量新开",
|
||||
2: "批量新开",
|
||||
3: "续费",
|
||||
4: "批量续费",
|
||||
5: "退款",
|
||||
}
|
||||
|
||||
export const PACKAGE_OPTIONS = [
|
||||
|
||||
75
src/lib/models/shuidi.ts
Normal file
75
src/lib/models/shuidi.ts
Normal file
@@ -0,0 +1,75 @@
|
||||
// ==================== 水滴静态:游戏列表 ====================
|
||||
|
||||
export type ShuidiGame = {
|
||||
id: number
|
||||
head?: number
|
||||
name: string
|
||||
}
|
||||
|
||||
// ==================== 水滴静态:套餐列表 ====================
|
||||
|
||||
export type ShuidiPackage = {
|
||||
Id: string
|
||||
TenantId: string
|
||||
ProductId: string
|
||||
Name: string
|
||||
PackageType: string
|
||||
Title: string | null
|
||||
Image: string
|
||||
Status: string
|
||||
Profile: string
|
||||
Price: string
|
||||
LinePrice: string
|
||||
DayPrice: string
|
||||
MinPrice: string
|
||||
DayCount: string
|
||||
OriginKey: string
|
||||
OriginName: string
|
||||
CreateTime: string
|
||||
UpdateTime: string
|
||||
DeleteTag: string
|
||||
IsTest: string
|
||||
package_name: string
|
||||
}
|
||||
|
||||
// 带宽档位(test / 2M / 4M / 10M / 20M)-> 该档位下的套餐(天卡/周卡/月卡...)
|
||||
export type ShuidiPackageGroup = Record<string, ShuidiPackage[]>
|
||||
|
||||
export type ShuidiPackageList = {
|
||||
game: ShuidiPackageGroup
|
||||
line: ShuidiPackageGroup
|
||||
}
|
||||
|
||||
// ==================== 水滴静态:线路列表 ====================
|
||||
|
||||
export type ShuidiLineLevel = {
|
||||
total: number
|
||||
line_level_en_name: string
|
||||
province: number | string
|
||||
}
|
||||
|
||||
export type ShuidiCity = {
|
||||
total: number
|
||||
discount?: number
|
||||
isp_class: number
|
||||
name: string
|
||||
province: string
|
||||
rid: string
|
||||
selected_num: number
|
||||
city?: string | number
|
||||
ping?: number
|
||||
}
|
||||
|
||||
export type ShuidiProvince = {
|
||||
total: number
|
||||
province_count: number
|
||||
ping: number
|
||||
line_levels: Record<string, ShuidiLineLevel>
|
||||
en_name: string
|
||||
citys: ShuidiCity[]
|
||||
}
|
||||
|
||||
export type ShuidiRegion = {
|
||||
surplus_out_total?: number
|
||||
province_surplus_outs: Record<string, ShuidiProvince>
|
||||
}
|
||||
@@ -25,11 +25,28 @@ export default defineConfig({
|
||||
target: "http://192.168.3.6:5000",
|
||||
bypass(req) {
|
||||
if (req.headers?.accept?.includes("text/html")) {
|
||||
return "/index.html"
|
||||
// 仅前端 SPA 路由回退 index.html;续费等后端老页面(rebuyindex/rebuy)
|
||||
// 需要直达后端渲染
|
||||
const pathname = new URL(req.url ?? "", "http://localhost").pathname
|
||||
const spaPaths = [
|
||||
"/product",
|
||||
"/product/buy",
|
||||
"/product/buy-static",
|
||||
"/product/http",
|
||||
"/product/routeros",
|
||||
]
|
||||
if (spaPaths.includes(pathname)) {
|
||||
return "/index.html"
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
"/script": "https://php-api.juip.com",
|
||||
"/jinyou": {
|
||||
target: "https://php-api.juip.com",
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
},
|
||||
"/http": {
|
||||
target: "https://php-api.juip.com",
|
||||
changeOrigin: true,
|
||||
|
||||
Reference in New Issue
Block a user