From ef0a68a7b400726cf6aa913ce6db7b1c4b08e805 Mon Sep 17 00:00:00 2001 From: dark Date: Mon, 19 Aug 2024 00:40:52 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0app=5Fpackage=E6=A8=A1?= =?UTF-8?q?=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 1 + src/pages/app/package/index.tsx | 441 ++++++++++++++++++++++++++++++++++++++++ src/pages/app/package/style.ts | 26 +++ src/service/app/package.ts | 25 +++ src/store/app/package.ts | 90 ++++++++ src/types/app/package.d.ts | 25 +++ src/utils/index.ts | 8 +- vite.config.ts | 11 + 8 files changed, 625 insertions(+), 2 deletions(-) create mode 100644 src/pages/app/package/index.tsx create mode 100644 src/pages/app/package/style.ts create mode 100644 src/service/app/package.ts create mode 100644 src/store/app/package.ts create mode 100644 src/types/app/package.d.ts diff --git a/package.json b/package.json index 1fed9a6..ba50a0b 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "axios": "^1.6.8", "bunshi": "^2.1.4", "dayjs": "^1.11.10", + "fast-copy": "^3.0.2", "fast-deep-equal": "^3.1.3", "i18next": "^23.11.2", "i18next-browser-languagedetector": "^7.2.1", diff --git a/src/pages/app/package/index.tsx b/src/pages/app/package/index.tsx new file mode 100644 index 0000000..fbf3e16 --- /dev/null +++ b/src/pages/app/package/index.tsx @@ -0,0 +1,441 @@ +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, +} from '@/store/app/package' +import { useEffect, useMemo, useState } from 'react' +import Action from '@/components/action/Action.tsx' +import { + BetaSchemaForm, + ProColumns, + 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 Switch from '@/components/switch' + +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 [ open, setOpen ] = useState(false) + const [ openFilter, setFilterOpen ] = useState(false) + const [ searchKey, setSearchKey ] = useState(search?.title) + + const columns = useMemo(() => { + return [ + { + 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', '包名必填') + } + ] + } + }, + + { + title: t(`${i18nPrefix}.columns.app_name`, '应用名'), + dataIndex: 'app_name', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '应用名必填') + } + ] + } + }, + + { + title: t(`${i18nPrefix}.columns.app_icon`, '应用图标'), + dataIndex: 'app_icon', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '应用图标必填') + } + ] + } + }, + + { + title: t(`${i18nPrefix}.columns.web_url`, '主页地址'), + dataIndex: 'web_url', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '主页地址必填') + } + ] + } + }, + + { + title: t(`${i18nPrefix}.columns.splash_url`, '启动图'), + dataIndex: 'splash_url', + }, + + { + title: t(`${i18nPrefix}.columns.conf`, '配置地址'), + dataIndex: 'conf', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '配置地址必填') + } + ] + } + }, + + { + title: t(`${i18nPrefix}.columns.jks_url`, '签名文件地址'), + dataIndex: 'jks_url', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', '签名文件地址必填') + } + ] + } + }, + + { + 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', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', 'key别名必填') + } + ] + } + }, + + { + title: t(`${i18nPrefix}.columns.store_pwd`, 'jks密码'), + dataIndex: 'store_pwd', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', 'jks密码必填') + } + ] + } + }, + + { + title: t(`${i18nPrefix}.columns.key_pwd`, 'key密码'), + dataIndex: 'key_pwd', + formItemProps: { + rules: [ + { + required: true, + message: t('message.required', 'key密码必填') + } + ] + } + }, + + { + title: t(`${i18nPrefix}.columns.status`, '状态'), + dataIndex: 'status', + valueType: 'select', + fieldProps: { + options: [ + { label: '未处理', value: 0 }, + { label: '队列中', value: 1 }, + { label: '打包中', value: 2 }, + { label: '打包成功', value: 3 }, + { label: '打包失败', value: 4 }, + ] + }, + render: (_text, record) => { + //0未处理 1队列中 2打包中 3打包成功 4打包失败 + return + } + }, + + { + title: t(`${i18nPrefix}.columns.message`, '打包信息'), + dataIndex: 'message', + }, + + { + title: t(`${i18nPrefix}.columns.x_86`, '32位'), + dataIndex: 'x_86', + valueType: 'switch', + render: (_text, record) => { + return + } + }, + + { + title: t(`${i18nPrefix}.columns.uid`, 'uid'), + dataIndex: 'uid', + hideInTable: true, + hideInSearch: true, + formItemProps: { hidden: true } + }, + + { + title: t(`${i18nPrefix}.columns.option`, '操作'), + key: 'option', + valueType: 'option', + fixed: 'right', + render: (_, record) => [ + { + form.setFieldsValue(record) + setOpen(true) + }}>{t('actions.edit')}, + { + deleteAppPackage(record.id ) + }} + title={t('message.deleteConfirm')}> + + {t('actions.delete', '删除')} + + + ] + } + ] as ProColumns[] + }, [ isDeleting, currentAppPackage, search ]) + + useEffect(() => { + + setSearchKey(search?.title) + filterForm.setFieldsValue(search) + + }, [ search ]) + + useEffect(() => { + if (isSuccess) { + setOpen(false) + } + }, [ isSuccess ]) + + return ( + + { + setSearch(prev => ({ + ...prev, + title: value + })) + }, + allowClear: true, + onChange: (e) => { + setSearchKey(e.target?.value) + }, + value: searchKey, + placeholder: t(`${i18nPrefix}.placeholder`, '输入应用打包名称') + }, + actions: [ + + + + ] + }} + scroll={{ + x: columns.length * 200, + y: 'calc(100vh - 290px)' + }} + search={false} + onRow={(record) => { + return { + className: cx({ + 'ant-table-row-selected': currentAppPackage?.id === record.id + }), + onClick: () => { + setAppPackage(record) + } + } + }} + dateFormatter="string" + loading={isLoading || isFetching} + dataSource={data?.rows ?? []} + columns={columns} + options={{ + reload: () => { + refetch() + }, + }} + pagination={{ + total: data?.total, + pageSize: search.pageSize, + current: search.page, + onShowSizeChange: (current: number, size: number) => { + setSearch({ + ...search, + pageSize: size, + page: current + }) + }, + onChange: (current, pageSize) => { + setSearch(prev => { + return { + ...prev, + page: current, + pageSize: pageSize, + } + }) + }, + }} + /> + { + setOpen(open) + }} + loading={isSubmitting} + onValuesChange={(values) => { + + }} + onFinish={async (values) => { + saveOrUpdate(values) + }} + columns={columns as ProFormColumnsType[]}/> + { + setFilterOpen(open) + }} + layout={'vertical'} + scrollToFirstError={true} + layoutType={'DrawerForm'} + drawerProps={{ + maskClosable: false, + mask: false, + }} + submitter={{ + searchConfig: { + resetText: t(`${i18nPrefix}.filter.reset`, '清空'), + submitText: t(`${i18nPrefix}.filter.submit`, '查询'), + }, + onReset: () => { + filterForm.resetFields() + }, + render: (props,) => { + return ( +
+ + + + +
+ ) + }, + + }} + onValuesChange={(values) => { + + }} + + onFinish={async (values) => { + //处理,变成数组 + Object.keys(values).forEach(key => { + if (typeof values[key] === 'string' && values[key].includes(',')) { + values[key] = values[key].split(',') + } + }) + + setSearch(values) + + }} + columns={unSetColumnRules(columns.filter(item => !item.hideInSearch)) as ProFormColumnsType[]}/> +
+ ) +} + +export default AppPackage \ No newline at end of file diff --git a/src/pages/app/package/style.ts b/src/pages/app/package/style.ts new file mode 100644 index 0000000..8774450 --- /dev/null +++ b/src/pages/app/package/style.ts @@ -0,0 +1,26 @@ +import { createStyles } from '@/theme' + +export const useStyle = createStyles(({ token, css, cx, prefixCls }, props: any) => { + const prefix = `${prefixCls}-${token?.proPrefix}-appPackage-list-page` + + const container = css` + .ant-table-cell{ + .ant-tag{ + padding-inline: 3px; + margin-inline-end: 3px; + } + } + .ant-table-empty { + .ant-table-body{ + height: calc(100vh - 350px) + } + } + .ant-pro-table-highlight{ + + } + ` + + return { + container: cx(prefix, props?.className, container), + } +}) \ No newline at end of file diff --git a/src/service/app/package.ts b/src/service/app/package.ts new file mode 100644 index 0000000..f5f7cb6 --- /dev/null +++ b/src/service/app/package.ts @@ -0,0 +1,25 @@ +import { createCURD } from '@/service/base.ts' +import { APP } from '@/types/app/package' +import request from '@/request.ts' +import { IPageResult } from '@/global' + +const appPackage = { + ...createCURD('/package'), + list: async (params: any) => { + return await request.get>(`/package/list`, { ...params }) + }, + //http://154.88.7.8:45321/package/v1/create + add: async (params: any) => { + return await request.post>(`/package/create`, { ...params }) + }, + //delete + delete: async (params: any) => { + return await request.post>(`/package/delete`, { ...params }) + }, + //package + package: async (params: any) => { + return await request.post>(`/package/package`, { ...params }) + }, +} + +export default appPackage \ No newline at end of file diff --git a/src/store/app/package.ts b/src/store/app/package.ts new file mode 100644 index 0000000..0cb28c3 --- /dev/null +++ b/src/store/app/package.ts @@ -0,0 +1,90 @@ +import { atom } from 'jotai' +import { IApiResult, IPage } from '@/global' +import { atomWithMutation, atomWithQuery, queryClientAtom } from 'jotai-tanstack-query' +import { message } from 'antd' +import { t } from 'i18next' +import { APP } from '@/types/app/package' +import aPPServ from '@/service/app/package' + +type SearchParams = IPage & { + key?: string + + [key: string]: any +} + +export const appPackageIdAtom = atom(0) + +export const appPackageIdsAtom = atom([]) + +export const appPackageAtom = atom(undefined as unknown as APP.IAppPackage ) + +export const appPackageSearchAtom = atom({ + key: '', + pageSize: 10, + page: 1, +} as SearchParams) + +export const appPackagePageAtom = atom({ + pageSize: 10, + page: 1, +}) + +export const appPackagesAtom = atomWithQuery((get) => { + return { + queryKey: [ 'appPackages', get(appPackageSearchAtom) ], + queryFn: async ({ queryKey: [ , params ] }) => { + return await aPPServ.list(params as SearchParams) + }, + select: res => { + const data = res.data + data.rows = data.rows?.map(row => { + return { + ...row, + //status: convertToBool(row.status) + } + }) + return data + } + } +}) + +//saveOrUpdateAtom +export const saveOrUpdateAppPackageAtom = atomWithMutation((get) => { + + return { + mutationKey: [ 'updateAppPackage' ], + mutationFn: async (data) => { + //data.status = data.status ? '1' : '0' + if (data.id === 0) { + return await aPPServ.add(data) + } + return await aPPServ.update(data) + }, + onSuccess: (res) => { + const isAdd = !!res.data?.id + message.success(t(isAdd ? 'message.saveSuccess' : 'message.editSuccess', '保存成功')) + + //更新列表 + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore fix + get(queryClientAtom).invalidateQueries({ queryKey: [ 'appPackages', get(appPackageSearchAtom) ] }) + + return res + } + } +}) + +export const deleteAppPackageAtom = atomWithMutation((get) => { + return { + mutationKey: [ 'deleteAppPackage' ], + mutationFn: async (ids: number) => { + return await aPPServ.delete({ id: ids } ?? get(appPackageIdsAtom)) + }, + onSuccess: (res) => { + message.success('message.deleteSuccess') + //更新列表 + get(queryClientAtom).invalidateQueries({ queryKey: [ 'appPackages', get(appPackageSearchAtom) ] }) + return res + } + } +}) diff --git a/src/types/app/package.d.ts b/src/types/app/package.d.ts new file mode 100644 index 0000000..356ed31 --- /dev/null +++ b/src/types/app/package.d.ts @@ -0,0 +1,25 @@ +export namespace APP { + export interface IAppPackage { + id: number; + package_name: string; + app_name: string; + app_icon: string; + web_url: string; + splash_url: string; + conf: string; + jks_url: string; + app_url: string; + splash_color: string; + key_alias: string; + store_pwd: string; + key_pwd: string; + created_by: number; + created_at: string; + updated_at: string; + updated_by: number; + status: number; + message: string; + x_86: number; + uid: number; + } +} \ No newline at end of file diff --git a/src/utils/index.ts b/src/utils/index.ts index e140431..db9ffb5 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -2,6 +2,8 @@ import { IMenu } from '@/types/system/menus' import { FiledNames, FlattenData, MenuItem } from '@/global' import { getIcon } from '@/components/icon' import { TreeDataNode, MenuItemProps } from 'antd' +import deepCopy from 'fast-copy' + //vite环境变量, 判断是否是开发环境 export const isDev = import.meta.env.MODE === 'development' @@ -177,7 +179,7 @@ export const getValueCount = (obj: any, filterObj: any = {}) => { export const unSetColumnRules = (columns: any[]) => { - return columns.map(col => { + return deepCopy(columns)?.map(col => { col.__ignoreRules = true if (col.formItemProps?.rules?.length) { col.formItemProps.rules = [] @@ -191,7 +193,9 @@ export const unSetColumnRules = (columns: any[]) => { } } } - return col + return { + ...col + } }) } diff --git a/vite.config.ts b/vite.config.ts index 341afe7..8fbf93c 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -26,6 +26,17 @@ export default defineConfig(({ mode }) => { origin: '*' }, proxy: { + '/api/v1/package': { + target: 'http://154.88.7.8:45321', + changeOrigin: true, + rewrite: (path) => { + //replace /api/v1/package to /package/v1 + //http://154.88.7.8:45321/package/v1/list + path = path.replace('/api/v1/package', '/package/v1') + console.log(path) + return path + } + }, '/api/v1/movie': { target: 'http://47.113.117.106:10000', changeOrigin: true,