Browse Source

Merge remote-tracking branch 'origin/main'

main
lk 3 months ago
parent
commit
431a401372
  1. 19
      src/components/r-form/index.tsx
  2. 24
      src/components/r-form/utils/index.tsx
  3. 23
      src/i18n.ts
  4. 6
      src/locales/lang/zh-CN.ts
  5. 77
      src/pages/app/plugin/index.tsx
  6. 15
      src/pages/app/plugin/style.ts
  7. 12
      src/service/app/package.ts
  8. 42
      src/store/app/package.ts
  9. 27
      src/types/app/package.d.ts
  10. 3
      src/types/r-form/model.d.ts
  11. 2
      src/utils/index.ts

19
src/components/r-form/index.tsx

@ -14,6 +14,7 @@ import { useApiContext } from '@/context.ts'
import { useDeepCompareEffect } from 'react-use'
import { RFormTypes } from '@/types/r-form/model'
import { ProCoreActionType } from '@ant-design/pro-utils/es/typing'
import { getI18nTitle } from '@/i18n.ts'
export interface RFormProps {
@ -66,7 +67,7 @@ const RForm = (
useDeepCompareEffect(() => {
let res = transformAntdTableProColumns(curdModal?.columns || [], propColumns)
let res = transformAntdTableProColumns(curdModal?.columns || [], propColumns, curdModal?.config?.i18n)
if (resolveColumns) {
res = resolveColumns(res)
}
@ -101,7 +102,7 @@ const RForm = (
formItemProps: { hidden: true }
} ].concat(res as any).concat([
{
title: '操作',
title: getI18nTitle(curdModal?.config?.i18n, { dataIndex: 'option', title: '操作' },),
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
@ -114,7 +115,7 @@ const RForm = (
} as any
])
setColumns(_columns)
}, [ curdModal?.columns, propColumns, renderColumnOptions, resolveColumns, deleteModel, form, isDeleting, setOpen, ])
}, [ curdModal?.columns, curdModal?.config?.i18n, propColumns, renderColumnOptions, resolveColumns, deleteModel, form, isDeleting, setOpen, ])
useEffect(() => {
if (apiCtx.isApi && apiCtx.api) {
@ -170,7 +171,7 @@ const RForm = (
})
setOpen(true)
}}
type={'primary'}>{'添加'}</Button>
type={'primary'}>{getI18nTitle('actions.add','添加')}</Button>
</>
const _renderActions = () => {
@ -209,7 +210,7 @@ const RForm = (
placeholder: '输入关键字搜索',
},*/
actions: [
<Tooltip key={'filter'} title={'高级查询'}>
<Tooltip key={'filter'} title={getI18nTitle('actions.advanceSearch','高级查询')}>
<Badge count={getValueCount(search)}>
<Button
onClick={() => {
@ -277,7 +278,7 @@ const RForm = (
form={form}
layout={'vertical'}
scrollToFirstError={true}
title={model?.id !== 0 ? '编辑' : '添加'}
title={model?.id !== 0 ? getI18nTitle('actions.edit','编辑') : getI18nTitle('actions.add','添加')}
{...formProps as any}
open={open}
onOpenChange={(open) => {
@ -291,7 +292,7 @@ const RForm = (
columns={columns as ProFormColumnsType[]}/>
<BetaSchemaForm
{...curdModal?.form}
title={'高级查询'}
title={getI18nTitle('actions.advanceSearch','高级查询')}
grid={true}
shouldUpdate={false}
width={500}
@ -313,8 +314,8 @@ const RForm = (
}}
submitter={{
searchConfig: {
resetText: '清空',
submitText: '查询',
resetText: getI18nTitle('actions.clear', '清空'),
submitText: getI18nTitle('actions.search', '查询'),
},
onReset: () => {
filterForm.resetFields()

24
src/components/r-form/utils/index.tsx

@ -6,6 +6,7 @@ import request from '@/request'
import { convertToBool } from '@/utils'
import { has, get } from 'lodash'
import { mapTree } from '@/utils/tree.ts'
import { getI18nTitle } from '@/i18n.ts'
const getValueType = (column: ProColumns) => {
@ -40,7 +41,7 @@ const getComponent = (column: ProColumns) => {
}
export const transformAntdTableProColumns = (columns: ProColumns[], overwriteColumns?: ProColumns[]) => {
export const transformAntdTableProColumns = (columns: ProColumns[], overwriteColumns?: ProColumns[], i18n?: string) => {
const overwriteKeys = [] as string[]
@ -56,27 +57,40 @@ export const transformAntdTableProColumns = (columns: ProColumns[], overwriteCol
return {
...item,
title: getI18nTitle(i18n!, item),
request: item.request ? async (params) => {
const { url: _url, method, params: p, fieldNames, resultPath } = item.request as unknown as RFormTypes.IRequest
const {
transform = true,
url: _url,
method,
params: p,
fieldNames,
resultPath
} = item.request as unknown as RFormTypes.IRequest
const { value, label, disabled: disabledKey, children = 'children' } = fieldNames || {}
const url = (_url.startsWith('/') || _url.startsWith('http')) ? _url : `/${_url}`
return request[method?.toLowerCase() || 'get'](url, {
...params,
...p,
}).then(res => {
try {
const data = resultPath && has(res.data, resultPath) ? get(res.data, resultPath) : res.data
if (!transform) {
return data
}
return mapTree(data || [], (i: any) => {
const disabled = disabledKey && has(i, disabledKey) ? get(i, disabledKey) : ('status' in i ? !convertToBool(i.status) : false)
const title = i.i18n ? getI18nTitle(i.i18n, i.label || i[label || 'name']) : i.label || i[label || 'name']
return {
title: i.label || i[label || 'name'],
label: i.label || i[label || 'name'],
title,
label: title,
value: i.value ?? i[value || 'id'],
disabled,
data: i
}
}, { children })
} catch (e) {

23
src/i18n.ts

@ -3,6 +3,7 @@ import i18n, { InitOptions, t } from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initReactI18next, useTranslation, } from 'react-i18next'
import { zh, en } from './locales'
import { ProColumns } from '@ant-design/pro-components'
const detectionOptions = {
// 探测器的选项
@ -45,6 +46,28 @@ export const initI18n = (options?: InitOptions) => {
}
export function getI18nTitle(key: string, defTitle: string): string;
export function getI18nTitle(key: string, column: ProColumns): string;
export function getI18nTitle(key: string, option: any): string {
if (option.dataIndex) {
let k = `${key}.columns.${option.dataIndex}`
if (option.i18n) {
k = option.i18n
}
//如果没有key也没有option.i18n, 说明没有i18n前缀, 直接返回
if (!option.i18n && !key) {
return option.title || option.label
}
return t(k, (option.title || option.label) as string)
}
if (!key) {
return option as unknown as string
}
return t(`${key}`, option as unknown as string)
}
export {
useTranslation, t
}

6
src/locales/lang/zh-CN.ts

@ -77,6 +77,12 @@ export default {
close: '关闭',
copy: '复制',
clickCopy: '点击复制',
resetPass: '重置密码',
save: '保存',
submit: '提交',
refresh: '刷新',
search: '查询',
advanceSearch: '高级查询',
},
message: {
infoTitle: '提示',

77
src/pages/app/plugin/index.tsx

@ -0,0 +1,77 @@
import {Button, Card, List, message, Select} from 'antd'
import React, {useState} from 'react'
import {useStyle} from './style'
import {useAtomValue} from "jotai";
import {appAllListAtom, appPluginBuyAtom, appPluginListAtom} from "@/store/app/package.ts";
import ListPageLayout from "@/layout/ListPageLayout.tsx";
import {DollarOutlined} from "@ant-design/icons";
import {APP} from "@/types/app/package";
const i18nPrefix = 'appPackages.list'
const AppPackage = () => {
const {styles, cx} = useStyle()
const {data} = useAtomValue(appPluginListAtom)
const {mutate: buyPlugin, isPending: buyPending} = useAtomValue(appPluginBuyAtom)
const {data: allApp} = useAtomValue(appAllListAtom)
const [whitelistApp, setWhitelistApp] = useState(0);
const buy = (item: APP.IAppPlugin) => {
if (item.code == "traffic") {
buyPlugin({plugin_id: item.id, code: item.code})
} else if (item.code == "whitelist") {
if (whitelistApp == 0) {
message.warning('请先选择应用')
return
}
buyPlugin({plugin_id: item.id, code: item.code, app_id: whitelistApp})
}
}
return (
<ListPageLayout className={styles.container}>
<List
grid={{
gutter: 16,
xs: 1,
sm: 2,
md: 2,
lg: 3,
xl: 4,
xxl: 6,
}}
dataSource={data?.rows}
renderItem={(item) => (
<List.Item>
<Card
title={item.name}
style={{height: 400}}
styles={{body: {height: 300, overflow: 'hidden'}}}
actions={[
item.code == "whitelist" ? (
<Select placeholder="请选择应用">
{allApp?.map((item) => (
<option key={item.id} value={item.id}>
{item.app_name}
</option>
))}</Select>
) : null,
<Button type="primary" icon={<DollarOutlined/>} onClick={() => buy(item)}
loading={buyPending}>
${item.month_price}
</Button>,
]}
>
<p>{item.description}</p>
</Card>
</List.Item>
)}
/>
</ListPageLayout>
)
}
export default AppPackage

15
src/pages/app/plugin/style.ts

@ -0,0 +1,15 @@
import { createStyles } from '@/theme'
export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => {
const prefix = `${prefixCls}-${token?.proPrefix}-appPackage-plugin-page`
const container = css`
.zcss-kfly0u{
background: transparent;
}
`
return {
container: cx(prefix, props?.className, container),
}
})

12
src/service/app/package.ts

@ -28,6 +28,18 @@ const appPackage = {
getPkgStatus: async (params: any) => {
return await request.get<APP.IAppPkgStatus>(`/package/status`, { ...params })
},
getPluginList: async () => {
return await request.get<IPageResult<APP.IAppPlugin>>(`/package/plugin/list`)
},
getAppAllList: async () => {
return await request.get<APP.IBaseArrRes<APP.IAppPackage>>(`/package/allList`)
},
buyPlugin: async (params: any) => {
return await request.post<APP.IAppBoolRes>(`/package/plugin/buy`, { ...params })
},
}
export default appPackage

42
src/store/app/package.ts

@ -114,3 +114,45 @@ export const packageAppAtom = atomWithMutation((get) => {
}
}
})
/**
*
*/
export const appPluginListAtom = atomWithQuery(() => {
return {
queryKey: [ 'appPlugin' ],
queryFn: async ({ queryKey: [ , ] }) => {
const list = await aPPServ.getPluginList()
return list.data
}
}
})
/**
*
*/
export const appPluginBuyAtom = atomWithMutation(() => {
return {
mutationKey: [ 'appPlugin' ],
mutationFn: async (param: any) => {
const list = await aPPServ.buyPlugin(param)
return list.data
},
onSuccess: () => {
message.success('购买成功')
}
}
})
/**
*
*/
export const appAllListAtom = atomWithQuery(() => {
return {
queryKey: [ 'appAllList' ],
queryFn: async ({ queryKey: [ , ] }) => {
const list = await aPPServ.getAppAllList()
return list.data.list
}
}
})

27
src/types/app/package.d.ts

@ -32,6 +32,13 @@ export namespace APP {
}
/**
* Bool类型返回结果
*/
export interface IBaseArrRes<T> {
list: T[];
}
/**
*
*/
export interface IAppPkgStatus {
@ -44,4 +51,24 @@ export namespace APP {
position: number;
total: number;
}
/**
*
*/
export interface IAppPlugin {
id: number;
name: string;
description: string;
status: number;
month_price: number;
quarter_price: number;
half_year_price: number;
year_price: number;
two_year_price: number;
three_year_price: number;
onetime_price: number;
reset_price: number;
extra: number; //备用字段,如果是流量套餐则是流量额度
code: string;
}
}

3
src/types/r-form/model.d.ts

@ -27,6 +27,9 @@ export namespace RFormTypes {
params?: any;
resultPath?: string;
//是否需要转换成下拉框的数据或者树形数据格式, 默认为true
transform: boolean;
fieldNames?: {
label: string;
value: string;

2
src/utils/index.ts

@ -216,3 +216,5 @@ export const genProTableColumnWidthProps = (width: string | number) => {
}
}
Loading…
Cancel
Save