From 815f558e03036bd2e33a7ac7b7dac64aa62196a4 Mon Sep 17 00:00:00 2001 From: cs Date: Mon, 2 Sep 2024 23:46:28 +0800 Subject: [PATCH 1/3] =?UTF-8?q?app=E6=89=93=E5=8C=85UI=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/pages/app/package/index.tsx | 379 +++++++++++++++++++++++++++++++--------- src/service/app/package.ts | 4 + src/types/app/package.d.ts | 4 + 3 files changed, 303 insertions(+), 84 deletions(-) diff --git a/src/pages/app/package/index.tsx b/src/pages/app/package/index.tsx index dbcdee6..6a5c1cd 100644 --- a/src/pages/app/package/index.tsx +++ b/src/pages/app/package/index.tsx @@ -1,11 +1,11 @@ -import { useTranslation } from '@/i18n.ts' -import { Button, Form, Popconfirm, Divider, Space, Tooltip, Badge } from 'antd' -import { useAtom, useAtomValue } from 'jotai' +import {useTranslation} from '@/i18n.ts' +import {Button, Form, Popconfirm, Divider, Space, Tooltip, Badge} from 'antd' +import {useAtom, useAtomValue} from 'jotai' import { deleteAppPackageAtom, saveOrUpdateAppPackageAtom, appPackageAtom, appPackagesAtom, appPackageSearchAtom, packageAppAtom, } from '@/store/app/package' -import { useEffect, useMemo, useState } from 'react' +import {useEffect, useMemo, useState} from 'react' import Action from '@/components/action/Action.tsx' import { BetaSchemaForm, @@ -13,199 +13,407 @@ import { ProFormColumnsType, } from '@ant-design/pro-components' import ListPageLayout from '@/layout/ListPageLayout.tsx' -import { useStyle } from './style' -import { FilterOutlined } from '@ant-design/icons' -import { getValueCount, unSetColumnRules } from '@/utils' -import { Table as ProTable } from '@/components/table' +import {useStyle} from './style' +import {FilterOutlined} from '@ant-design/icons' +import {getValueCount, unSetColumnRules} from '@/utils' +import {Table as ProTable} from '@/components/table' import Switch from '@/components/switch' +import aPPServ from "@/service/app/package.ts"; +import {useDialog} from "@/components/dialog"; const i18nPrefix = 'appPackages.list' const AppPackage = () => { - const { styles, cx } = useStyle() - const { t } = useTranslation() - const [ form ] = Form.useForm() - const [ filterForm ] = Form.useForm() - const { mutate: saveOrUpdate, isPending: isSubmitting, isSuccess } = useAtomValue(saveOrUpdateAppPackageAtom) - const [ search, setSearch ] = useAtom(appPackageSearchAtom) - const [ currentAppPackage, setAppPackage ] = useAtom(appPackageAtom) - const { data, isFetching, isLoading, refetch } = useAtomValue(appPackagesAtom) - const { mutate: deleteAppPackage, isPending: isDeleting } = useAtomValue(deleteAppPackageAtom) - const { mutate: packageApp, isPending: packageing } = useAtomValue(packageAppAtom) - - const [ open, setOpen ] = useState(false) - const [ openFilter, setFilterOpen ] = useState(false) - const [ searchKey, setSearchKey ] = useState(search?.title) + const {styles, cx} = useStyle() + const {t} = useTranslation() + const [form] = Form.useForm() + const [filterForm] = Form.useForm() + const {mutate: saveOrUpdate, isPending: isSubmitting, isSuccess} = useAtomValue(saveOrUpdateAppPackageAtom) + const [search, setSearch] = useAtom(appPackageSearchAtom) + const [currentAppPackage, setAppPackage] = useAtom(appPackageAtom) + const {data, isFetching, isLoading, refetch} = useAtomValue(appPackagesAtom) + const {mutate: deleteAppPackage, isPending: isDeleting} = useAtomValue(deleteAppPackageAtom) + const {mutate: packageApp, isPending: packageing} = useAtomValue(packageAppAtom) + + const [open, setOpen] = useState(false) + const [openFilter, setFilterOpen] = useState(false) + const [searchKey, setSearchKey] = useState(search?.title) + const [trafficEnabled, setTrafficEnabled] = useState(false); + + const [, dialog, openDialog] = useDialog({ + title: '提示', + children:
您还未购买流量套餐!
, + okText: '去购买', + onOk: () => { - const columns = useMemo(() => { + } + }) + + const handleTrafficSwitchClick = async () => { + const data = await aPPServ.check() + setTrafficEnabled(data.data.success) + if (!data.data.success) { + openDialog() + } + }; + + const drawerColumns = useMemo(() => { return [ { title: 'ID', dataIndex: 'id', hideInTable: true, hideInSearch: true, - formItemProps: { hidden: true } + formItemProps: {hidden: true} }, { - title: t(`${i18nPrefix}.columns.package_name`, '包名'), - dataIndex: 'package_name', + title: t(`${i18nPrefix}.columns.app_name`, '应用名称'), + dataIndex: 'app_name', + valueType: 'text', + fieldProps: { + maxLength: 10, + showCount: true, + }, formItemProps: { + tooltip: '手机端建议不超过6个字', rules: [ { required: true, - message: t('message.required', '包名必填') + message: t('message.required', '应用名称必填') } ] } }, - { - title: t(`${i18nPrefix}.columns.app_name`, '应用名'), - dataIndex: 'app_name', + title: t(`${i18nPrefix}.columns.package_name`, '包名'), + dataIndex: 'package_name', + valueType: 'text', + fieldProps: { + placeholder: '例: com.baidu.app', + maxLength: 20, + showCount: true, + }, formItemProps: { rules: [ { required: true, - message: t('message.required', '应用名必填') + message: t('message.required', '包名必填') } ] } }, - { - title: t(`${i18nPrefix}.columns.app_icon`, '应用图标'), + title: t(`${i18nPrefix}.columns.app_icon`, '应用图标链接'), dataIndex: 'app_icon', + valueType: 'input', formItemProps: { + tooltip: '建议图标最大尺寸512x512,格式png,Win应用可以直接用favicon.ico链接', rules: [ { required: true, message: t('message.required', '应用图标必填') + }, + { + pattern: /^(http|https):\/\/[^\s$.?#].[^\s]*$/, + message: t('message.invalidUrl', '请输入有效的URL') } ] } }, - { - title: t(`${i18nPrefix}.columns.web_url`, '主页地址'), + title: t(`${i18nPrefix}.columns.web_url`, '应用主页链接'), dataIndex: 'web_url', + valueType: 'input', formItemProps: { + tooltip: '填自己网站的链接', rules: [ { required: true, - message: t('message.required', '主页地址必填') + message: t('message.required', '应用主页链接必填') + }, + { + pattern: /^(http|https):\/\/[^\s$.?#].[^\s]*$/, + message: t('message.invalidUrl', '请输入有效的URL') } ] } }, - { - title: t(`${i18nPrefix}.columns.splash_url`, '启动图'), - dataIndex: 'splash_url', + title: t(`${i18nPrefix}.columns.os`, '目标系统'), + dataIndex: 'os', + valueType: 'select', + fieldProps: { + options: [ + {label: 'Android', value: 0}, + {label: 'Windows', value: 1} + ], + defaultValue: 0, + allowClear: false + }, + render: (_text, record) => { + //0未处理 1队列中 2打包中 3打包成功 4打包失败 + return + }, + formItemProps: { + rules: [ + { + required: true, + } + ] + } }, - { - title: t(`${i18nPrefix}.columns.conf`, '配置地址'), - dataIndex: 'conf', + title: t(`${i18nPrefix}.columns.enable_traffic`, '是否启用流量加速功能'), + dataIndex: 'enable_traffic', + valueType: 'switch', + fieldProps: { + onClick: handleTrafficSwitchClick, + checked: trafficEnabled + }, + formItemProps: { + tooltip: '防屏蔽功能,需先购买流量套餐', + } + }, + { + title: t(`${i18nPrefix}.columns.x_86`, '是否支持32位操作系统'), + dataIndex: 'x_86', + valueType: 'switch', formItemProps: { + tooltip: 'Android则是arm-v7a', + } + }, + { + title: t(`${i18nPrefix}.columns.splash_url`, 'Android启动页图片链接'), + dataIndex: 'splash_url', + valueType: 'input', + formItemProps: { + tooltip: '启动页的图片,只支持Android', rules: [ { - required: true, - message: t('message.required', '配置地址必填') + pattern: /^(http|https):\/\/[^\s$.?#].[^\s]*$/, + message: t('message.invalidUrl', '请输入有效的URL') } ] } }, - { - title: t(`${i18nPrefix}.columns.jks_url`, '签名文件地址'), + title: t(`${i18nPrefix}.columns.splash_color`, 'Android主题色'), + dataIndex: 'splash_color', + valueType: 'color', + formItemProps: { + tooltip: '建议选择启动页图片的底色,效果更美观,只支持Android', + } + }, + { + title: t(`${i18nPrefix}.columns.jks_url`, 'Android签名文件链接'), dataIndex: 'jks_url', + valueType: 'input', formItemProps: { + tooltip: 'jks签名文件url,不填则使用默认的签名', rules: [ { - required: true, - message: t('message.required', '签名文件地址必填') + pattern: /^(http|https):\/\/[^\s$.?#].[^\s]*$/, + message: t('message.invalidUrl', '请输入有效的URL') } ] } }, - { - title: t(`${i18nPrefix}.columns.app_url`, '应用地址'), - dataIndex: 'app_url', + title: t(`${i18nPrefix}.columns.store_pwd`, 'Android签名文件密码'), + dataIndex: 'store_pwd', + valueType: 'password', + formItemProps: { + tooltip: '如果自定义jks签名文件,此项必填', + rules: [ + { + validator: (_, value, callback) => { + form.validateFields(['jks_url']) + .then(() => { + const jksUrl = form.getFieldValue('jks_url'); + if (jksUrl && !value) { + callback(t('message.required', '密码必填')); + } else { + callback(); + } + }) + .catch(() => callback()); + } + } + ] + } }, - { - title: t(`${i18nPrefix}.columns.splash_color`, 'App背景色'), - dataIndex: 'splash_color', + title: t(`${i18nPrefix}.columns.key_alias`, 'Android密钥别名'), + dataIndex: 'key_alias', + valueType: 'input', + formItemProps: { + tooltip: '如果自定义jks签名文件,此项必填', + rules: [ + { + validator: (_, value, callback) => { + form.validateFields(['jks_url']) + .then(() => { + const jksUrl = form.getFieldValue('jks_url'); + if (jksUrl && !value) { + callback(t('message.required', '别名必填')); + } else { + callback(); + } + }) + .catch(() => callback()); + } + } + ] + } }, + { + title: t(`${i18nPrefix}.columns.key_pwd`, 'Android密钥密码'), + dataIndex: 'key_pwd', + valueType: 'password', + formItemProps: { + tooltip: '如果自定义jks签名文件,此项必填', + rules: [ + { + validator: (_, value, callback) => { + form.validateFields(['jks_url']) + .then(() => { + const jksUrl = form.getFieldValue('jks_url'); + if (jksUrl && !value) { + callback(t('message.required', '密码必填')); + } else { + callback(); + } + }) + .catch(() => callback()); + } + } + ] + } + }, + ] as ProColumns[] + }, [isDeleting, currentAppPackage, search]) + const columns = useMemo(() => { + return [ { - title: t(`${i18nPrefix}.columns.key_alias`, 'key别名'), - dataIndex: 'key_alias', + title: 'ID', + dataIndex: 'id', + hideInTable: true, + hideInSearch: true, + formItemProps: {hidden: true} + }, + { + title: t(`${i18nPrefix}.columns.package_name`, '包名'), + dataIndex: 'package_name', formItemProps: { rules: [ { required: true, - message: t('message.required', 'key别名必填') + message: t('message.required', '包名必填') } ] } }, { - title: t(`${i18nPrefix}.columns.store_pwd`, 'jks密码'), - dataIndex: 'store_pwd', + title: t(`${i18nPrefix}.columns.app_name`, '应用名'), + dataIndex: 'app_name', formItemProps: { rules: [ { required: true, - message: t('message.required', 'jks密码必填') + message: t('message.required', '应用名必填') } ] } }, { - title: t(`${i18nPrefix}.columns.key_pwd`, 'key密码'), - dataIndex: 'key_pwd', + title: t(`${i18nPrefix}.columns.app_icon`, '应用图标'), + dataIndex: 'app_icon', formItemProps: { rules: [ { required: true, - message: t('message.required', 'key密码必填') + message: t('message.required', '应用图标必填') } ] } }, { - title: t(`${i18nPrefix}.columns.status`, '状态'), - dataIndex: 'status', + title: t(`${i18nPrefix}.columns.web_url`, '主页地址'), + dataIndex: 'web_url', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '主页地址必填') + } + ] + } + }, + { + title: t(`${i18nPrefix}.columns.os`, '目标系统'), + dataIndex: 'os', valueType: 'select', fieldProps: { options: [ - { label: '未处理', value: 0 }, - { label: '队列中', value: 1 }, - { label: '打包中', value: 2 }, - { label: '打包成功', value: 3 }, - { label: '打包失败', value: 4 }, + {label: 'Android', value: 0}, + {label: 'Windows', value: 1} + ] + }, + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', 'key密码必填') + } ] }, render: (_text, record) => { //0未处理 1队列中 2打包中 3打包成功 4打包失败 - return + return } }, + { + title: t(`${i18nPrefix}.columns.splash_url`, '启动图'), + dataIndex: 'splash_url', + }, + { + title: t(`${i18nPrefix}.columns.jks_url`, '签名文件地址'), + dataIndex: 'jks_url', + }, + + { + title: t(`${i18nPrefix}.columns.app_url`, '应用地址'), + dataIndex: 'app_url', + }, + + { + title: t(`${i18nPrefix}.columns.splash_color`, 'App背景色'), + dataIndex: 'splash_color', + }, + + { + title: t(`${i18nPrefix}.columns.key_alias`, 'key别名'), + dataIndex: 'key_alias', + }, { - title: t(`${i18nPrefix}.columns.message`, '打包信息'), - dataIndex: 'message', + title: t(`${i18nPrefix}.columns.store_pwd`, 'jks密码'), + dataIndex: 'store_pwd', }, { - title: t(`${i18nPrefix}.columns.x_86`, '32位'), + title: t(`${i18nPrefix}.columns.key_pwd`, 'key密码'), + dataIndex: 'key_pwd', + }, + { + title: t(`${i18nPrefix}.columns.x_86`, '是否支持32位'), dataIndex: 'x_86', valueType: 'switch', render: (_text, record) => { @@ -218,7 +426,7 @@ const AppPackage = () => { dataIndex: 'uid', hideInTable: true, hideInSearch: true, - formItemProps: { hidden: true } + formItemProps: {hidden: true} }, { @@ -230,7 +438,7 @@ const AppPackage = () => { { packageApp(record) }} @@ -257,20 +465,20 @@ const AppPackage = () => { ] } ] as ProColumns[] - }, [ isDeleting, currentAppPackage, search ]) + }, [isDeleting, currentAppPackage, search]) useEffect(() => { setSearchKey(search?.title) filterForm.setFieldsValue(search) - }, [ search ]) + }, [search]) useEffect(() => { if (isSuccess) { setOpen(false) } - }, [ isSuccess ]) + }, [isSuccess]) return ( @@ -378,13 +586,13 @@ const AppPackage = () => { setOpen(open) }} loading={isSubmitting} - onValuesChange={(values) => { + onValuesChange={() => { }} onFinish={async (values) => { saveOrUpdate(values) }} - columns={columns as ProFormColumnsType[]}/> + columns={drawerColumns as ProFormColumnsType[]}/> { }, render: (props,) => { return ( -
+