From b780c37a570ab8af1dc48a8f15ec9688759f602e Mon Sep 17 00:00:00 2001 From: Lei OT Date: Mon, 11 Aug 2025 16:36:53 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E4=BA=A7=E5=93=81=E7=AE=A1=E7=90=86):=20?= =?UTF-8?q?=E5=A4=8D=E5=88=B6=E6=8C=87=E5=AE=9A=E4=BA=A7=E5=93=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/zh/products.json | 4 +- src/components/ProductsSelector.jsx | 56 ++++++++++ src/views/products/Detail/CopyProducts.jsx | 120 ++++++++++++++++----- 3 files changed, 151 insertions(+), 29 deletions(-) create mode 100644 src/components/ProductsSelector.jsx diff --git a/public/locales/zh/products.json b/public/locales/zh/products.json index 7bfe0e1..b611f82 100644 --- a/public/locales/zh/products.json +++ b/public/locales/zh/products.json @@ -1,5 +1,6 @@ { "ProductType": "项目类型", + "ProductName": "产品名称", "ContractRemarks": "合同备注", "versionHistory": "查看历史", "versionPublished": "已发布的", @@ -84,7 +85,8 @@ "withQuote": "是否复制报价", "requiredVendor": "请选择目标供应商", "requiredTypes": "请选择产品类型", - "requiredDept": "请选择所属小组" + "requiredDept": "请选择所属小组", + "copyTo": "复制到" }, "Validation": { "adultPrice": "请输入成人价", diff --git a/src/components/ProductsSelector.jsx b/src/components/ProductsSelector.jsx new file mode 100644 index 0000000..a3d79cf --- /dev/null +++ b/src/components/ProductsSelector.jsx @@ -0,0 +1,56 @@ +import { useEffect, useState } from 'react'; +import { Select, Spin } from 'antd'; +import { fetchJSON } from '@/utils/request'; +import { HT_HOST } from '@/config'; +import { useTranslation } from 'react-i18next'; +import { groupBy } from '@/utils/commons'; + +// 产品列表 +export const fetchAgencyProductsList = async (params) => { + const map = { title: 'label', id: 'value' }; + const { errcode, result } = await fetchJSON(`${HT_HOST}/Service_BaseInfoWeb/travel_agency_products`, params); + const byTypes = errcode !== 0 ? {} : (groupBy(result.products, (row) => row.info.product_type_name)); + // console.log(byTypes) + return Object.keys(byTypes).map((type_name) => ({ lable: type_name, title: type_name, key: type_name, options: byTypes[type_name].map(row => ({...row, label: `${row.info.code} : ${row.info.title}`, value: row.info.id})) })); +}; + +const ProductsSelector = ({ params, ...props }) => { + const { t } = useTranslation(); + const [fetching, setFetching] = useState(false); + const [options, setOptions] = useState([]); + + const fetchAction = async () => { + setOptions([]); + setFetching(true); + const data = await fetchAgencyProductsList(params); + // console.log(data) + setOptions(data); + setFetching(false); + return data; + }; + useEffect(() => { + fetchAction(); + + return () => {}; + }, []); + + return ( + <> + + + ); +}; +export default ProductsSelector; diff --git a/src/views/products/Detail/CopyProducts.jsx b/src/views/products/Detail/CopyProducts.jsx index 40fbca5..feccf17 100644 --- a/src/views/products/Detail/CopyProducts.jsx +++ b/src/views/products/Detail/CopyProducts.jsx @@ -1,5 +1,5 @@ import { useState, useEffect } from 'react'; -import { App, Form, Modal, DatePicker, Divider, Switch } from 'antd'; +import { App, Form, Modal, DatePicker, Divider, Switch, Space, Flex } from 'antd'; import { isEmpty, objectMapper } from '@/utils/commons'; import { useTranslation } from 'react-i18next'; @@ -13,43 +13,102 @@ import { copyAgencyDataAction } from '@/stores/Products/Index'; import useAuthStore from '@/stores/Auth'; import RequireAuth from '@/components/RequireAuth'; import { PERM_PRODUCTS_MANAGEMENT } from '@/config'; +import ProductsSelector from '@/components/ProductsSelector'; dayjs.extend(arraySupport); export const CopyProductsForm = ({ action, initialValues, onFormInstanceReady, source, ...props }) => { const { t } = useTranslation(); const [form] = Form.useForm(); + const { + sourceAgency: { travel_agency_id }, + sourceYear: use_year, + } = source; const isPermitted = useAuthStore((state) => state.isPermitted); + const [typeDisabled, setTypeDisabled] = useState(false); useEffect(() => { onFormInstanceReady(form); }, []); const onValuesChange = (changeValues, allValues) => {}; + return ( -
- {action === '#' && - - } - - - - {action === '#' && - - + + +
+ {action === '#' && ( + + + + + + )} + ({ + warningOnly: true, + validator: async () => { + if (!isEmpty(getFieldValue('products_list'))) { + // setTypeDisabled(true); + return Promise.reject(new Error(t(`⚠勾选了复制的产品名称之后 🔽, 将忽略选择的类型 🔼`))); + } + // setTypeDisabled(false); + return Promise.resolve(); + }, + }), + ]}> + + + + + + {t('products:CopyFormMsg.copyTo')}: + {action === '#' && ( + + + + )} + + + + + + + + + + {/* disabledDate={(current) => current <= dayjs([source.sourceYear, 12, 31])} */} + + + + + + +
+ + + {() => ( +
+ {!isEmpty(form.getFieldValue('products_list')) && 已选择的产品 预览:} + {(form.getFieldValue('products_list') || []).map((item, index) => ( +
+ {index + 1}. {item.label} +
+ ))} +
+ )}
-
} - - - - - - {/* disabledDate={(current) => current <= dayjs([source.sourceYear, 12, 31])} */} - - - - +
); }; @@ -76,9 +135,10 @@ const formValuesMapper = (values) => { }, }, 'with_quote': { key: 'with_quote', transform: (value) => (value ? 1 : 0) }, + 'products_list': { key: 'product_id_list', transform: (value) => (Array.isArray(value) ? value.map((ele) => ele.key).join(',') : value ? value.value : '') }, }; let dest = {}; - const { agency, year, ...omittedValue } = values; + const { agency, year, products_list, ...omittedValue } = values; dest = { ...omittedValue, ...objectMapper(values, destinationObject) }; for (const key in dest) { if (Object.prototype.hasOwnProperty.call(dest, key)) { @@ -101,15 +161,18 @@ export const CopyProductsFormModal = ({ source, action = '#' | 'o', open, onSubm const handleCopyAgency = async (param) => { param.target_agency = isEmpty(param.target_agency) ? source.sourceAgency.travel_agency_id : param.target_agency; setCopyLoading(true); - // console.log(param); + // debug: + // console.log('ready params', param); + // setCopyLoading(false); + // throw new Error('暂不支持复制'); // const toID = param.target_agency; - const success = await copyAgencyDataAction({...param, source_agency: source.sourceAgency.travel_agency_id}).catch(ex => { + const success = await copyAgencyDataAction({ ...param, source_agency: source.sourceAgency.travel_agency_id }).catch((ex) => { notification.error({ message: 'Notification', description: ex.message, placement: 'top', duration: 4, - }) + }); }); setCopyLoading(false); success ? message.success(t('Success')) : message.error(t('Failed')); @@ -122,7 +185,7 @@ export const CopyProductsFormModal = ({ source, action = '#' | 'o', open, onSubm }; return ( - {