diff --git a/package.json b/package.json index b6fdbc82e164494a311991d1576472d786f6775a..34b3eefdc66d608fc3217e8d76517f16c66888f6 100644 --- a/package.json +++ b/package.json @@ -10,10 +10,7 @@ "scripts": { "dev": "vite --mode development", "build": "node --max_old_space_size=20480 ./node_modules/.bin/vite build", - "build:beta": "vite build --mode beta", - "build:release": "vite build --mode release", - "build:legacy": "vite build --mode legacy ", - "build:deploy": "vite build --base=/vite-react-ts/", + "build:win": "cross-env LIMIT=40960 increase-memory-limit && vite build", "lint": "npm run lint:js && npm run lint:prettier && npm run tsc", "lint-staged": "lint-staged", "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ", @@ -76,6 +73,7 @@ "antd": "^4.24.4", "axios": "^1.1.2", "bytemd": "^1.21.0", + "cross-env": "^7.0.3", "crypto-js": "^4.1.1", "dayjs": "^1.10.6", "devextreme": "^23.1.6", @@ -86,6 +84,7 @@ "for-editor": "^0.3.5", "handsontable": "^14.0.0", "html2canvas": "^1.4.1", + "increase-memory-limit": "^1.0.7", "jol-player": "^4.0.0", "pako": "^2.1.0", "pinyin-pro": "^3.19.6", diff --git a/src/components/Common/ExecutorShowComp/index.tsx b/src/components/Common/ExecutorShowComp/index.tsx index d80256f0f6c30df4ff452223a109de678adca6e4..77e78d81e7af8f2aa3824d3fe428a36a7aadfd68 100644 --- a/src/components/Common/ExecutorShowComp/index.tsx +++ b/src/components/Common/ExecutorShowComp/index.tsx @@ -462,7 +462,7 @@ const FormChangesTable: React.FC = (props) => { interface FieldChangeTableProps { work: IWork; formChange: model.FormChange; - finished: (e:model.FieldChange[]) => void; + finished: (e: model.FieldChange[]) => void; } export const FieldChangeTable: React.FC = (props) => { @@ -475,8 +475,8 @@ export const FieldChangeTable: React.FC = (props) => { title={'配置变更字段'} width={'80vw'} destroyOnClose - onCancel={()=>props.finished(fieldChanges)} - onOk={()=>props.finished(fieldChanges)}> + onCancel={() => props.finished(fieldChanges)} + onOk={() => props.finished(fieldChanges)}> search={false} options={false} @@ -758,6 +758,16 @@ export const ExecutorForm: React.FC = (props) => { return <>{formRef.current?.getFieldValue('beforeName')}; }, }, + { + title: '使用规则计算变动值', + dataIndex: 'switch', + valueType: 'switch', + width: 'md', + colProps: { + xs: 12, + md: 20, + }, + }, { title: '变动后值', dataIndex: 'after', @@ -767,7 +777,11 @@ export const ExecutorForm: React.FC = (props) => { }, renderFormItem: (_) => { const id = formRef.current?.getFieldValue('id'); + const isRule = formRef.current?.getFieldValue('switch'); const field = fields.find((item) => item.id == id); + if (isRule && field) { + field.valueType = '描述型'; + } if (field) { const clone = cloneField(field); return ( diff --git a/src/components/DataStandard/WorkForm/Viewer/customItem/currentTarget.tsx b/src/components/DataStandard/WorkForm/Viewer/customItem/currentTarget.tsx index c561741778468da53bae20405064c15831de8ff5..7a40101947774c63ef16caf4bd53e8b78ddd79b8 100644 --- a/src/components/DataStandard/WorkForm/Viewer/customItem/currentTarget.tsx +++ b/src/components/DataStandard/WorkForm/Viewer/customItem/currentTarget.tsx @@ -23,11 +23,12 @@ const CurrentTargetItem: React.FC = (props) => { return ( { diff --git a/src/config/column.tsx b/src/config/column.tsx index 6d57f3e53b0a22aa5eea53e54a2ba2314dc93a86..c365aeec01739ebc1b62ed823862e56979389a95 100644 --- a/src/config/column.tsx +++ b/src/config/column.tsx @@ -281,7 +281,7 @@ export const FullEntityColumns = (fields: model.FieldModel[], typeName?: string) export const FullProperties = (typeName: string) => { switch (typeName) { case '虚拟商品': - return ProductProperties(); + return VirtuallyProperties(ProductProperties()); case '实体商品': return PhysicalProperties(ProductProperties()); case '报表数据': @@ -304,15 +304,28 @@ export const PhysicalProperties = (props: schema.XProperty[]) => { id: 'count', name: '实体商品数量', code: 'count', - valueType: '描述型', + valueType: '数值型', remark: '实体商品数量', }, + { + id: 'isMultiple', + name: '是否能购买多件', + code: 'isMultiple', + valueType: '描述型', + remark: '是否能购买多件', + }, + { + id: 'orderCount', + name: '下单数量', + code: 'orderCount', + valueType: '数值型', + remark: '下单数量', + }, ...props, ] as schema.XProperty[]; }; -/** 商品属性 */ -export const ProductProperties = () => { +export const VirtuallyProperties = (props: schema.XProperty[]) => { return [ { id: 'icons', @@ -321,6 +334,13 @@ export const ProductProperties = () => { valueType: '附件型', remark: '图标组', }, + ...props, + ] as schema.XProperty[]; +}; + +/** 商品属性 */ +export const ProductProperties = () => { + return [ { id: 'title', name: '商品名称', diff --git a/src/executor/open/mallTemplate/components/dataDetails/index.tsx b/src/executor/open/mallTemplate/components/dataDetails/index.tsx index 56117cddd7b78baf877db06e3d76d3c4b5af41ea..0e00a80ab2780c3d34fc4afa7908a6267a7fda75 100644 --- a/src/executor/open/mallTemplate/components/dataDetails/index.tsx +++ b/src/executor/open/mallTemplate/components/dataDetails/index.tsx @@ -1,4 +1,4 @@ -import React, { memo, useCallback, useState, useEffect } from 'react'; +import React, { memo, useCallback } from 'react'; import { Modal, Image, Tabs, Carousel, Skeleton } from 'antd'; import type { schema, model } from '@/ts/base'; import { IMallTemplate } from '@/ts/core/thing/standard/page/mallTemplate'; @@ -8,23 +8,16 @@ interface IDataDetails { data: schema.XProduct; current: IMallTemplate; onCancel: () => void; + onAddCar: () => void; + onPurchase: (selectedRows: schema.XProduct[]) => void; } -const DataDetails = ({ current, data, onCancel }: IDataDetails) => { - const [staging, setStaging] = useState( - current.shoppingCar.products.some((a) => a.id == data.id), - ); +const DataDetails = ({ current, data, onCancel, onAddCar, onPurchase }: IDataDetails) => { const getImage = useCallback((type: string, num?: number) => { let images: model.FileItemShare[] = []; images = JSON.parse(data[type] || '[]'); return num ? images.splice(0, num) : images; }, []); - useEffect(() => { - const id = current.shoppingCar.subscribe(() => { - setStaging(current.shoppingCar.products.some((a) => a.id == data.id)); - }); - return () => current.shoppingCar.unsubscribe(id); - }, []); const evaluateData = [ { key: 1, @@ -173,7 +166,7 @@ const DataDetails = ({ current, data, onCancel }: IDataDetails) => {
{current.metadata.template === 'dataTemplate' ? ( } className={cls.productImg} @@ -203,7 +196,7 @@ const DataDetails = ({ current, data, onCancel }: IDataDetails) => {
{data.title || '[未设置名称]'}
{data.remark}
- {current.metadata.mode !== 'sharing' && ( + {data.mode !== '共享' && (
¥{data.price || 0}
{0}人已买
@@ -212,21 +205,13 @@ const DataDetails = ({ current, data, onCancel }: IDataDetails) => {
- {current.metadata.mode === 'sharing' ? ( + {data.mode === '共享' ? ( 立即申领 ) : ( - {}}>立即购买 + 立即购买 )}
-
{ - if (staging) { - current.shoppingCar.remove(data); - } else { - current.shoppingCar.create(data); - } - }}> +
加入购物车
diff --git a/src/executor/open/mallTemplate/index.module.less b/src/executor/open/mallTemplate/index.module.less index 2d970461fa92c65d36448ce307c04e01dc11cf67..56dd5c7f4462515d31bce5f07f01f6d38901a18f 100644 --- a/src/executor/open/mallTemplate/index.module.less +++ b/src/executor/open/mallTemplate/index.module.less @@ -70,7 +70,7 @@ } .introduce { font-size: 12px; - color: #424A57; + color: #424a57; font-weight: 500; } } @@ -121,8 +121,10 @@ } } -.dataSharing { +.physical { padding: 0px 20px; + height: calc(100vh - 46px) !important; + overflow-y: scroll; .content { width: 100%; display: flex; @@ -176,47 +178,52 @@ justify-content: space-between; gap: 10px; > * { - flex: 1 + flex: 1; } } } } - .carItem { display: flex; - width: 100%; - margin-top: 12px; - .image { - display: flex; - justify-content: center; - width: 80px; - height: 80px; - line-height: 24px; - color: #15181d; - font-size: 14px; - .ant-skeleton-image { - width: 100%; - } - } - .content { - margin-left: 10px; + justify-content: space-between; + align-items: center; + margin-left: 16px; + .carItemInfo { display: flex; - flex-direction: column; - .title { + width: 100%; + margin-top: 12px; + .image { display: flex; - justify-content: space-between; - font-weight: bold; + justify-content: center; + width: 80px; + height: 80px; + line-height: 24px; + color: #15181d; + font-size: 14px; + .ant-skeleton-image { + width: 100%; + } } - .tag { + .content { margin-left: 10px; - } - .remark { - margin-top: 4px; - font-size: 12px; - color: #666666; - } - .amount { - font-weight: bold; + display: flex; + flex-direction: column; + .title { + display: flex; + justify-content: space-between; + font-weight: bold; + } + .tag { + margin-left: 10px; + } + .remark { + margin-top: 4px; + font-size: 12px; + color: #666666; + } + .amount { + font-weight: bold; + } } } } diff --git a/src/executor/open/mallTemplate/pages/index.tsx b/src/executor/open/mallTemplate/pages/index.tsx index 086698153d0fdbfc9de1d0f0074f0ee55919e6d5..c7cc23a896246f682891ac54d4f77f08eb9062a6 100644 --- a/src/executor/open/mallTemplate/pages/index.tsx +++ b/src/executor/open/mallTemplate/pages/index.tsx @@ -11,6 +11,7 @@ import { Empty, Input, Layout, + message, Pagination, Row, Space, @@ -20,34 +21,98 @@ import { Content, Header } from 'antd/lib/layout/layout'; import { ScrollView, TreeView } from 'devextreme-react'; import React, { ReactNode, useEffect, useRef, useState } from 'react'; import { Product } from '../widget/product'; +import { Virtually } from '../widget/virtually'; import cls from './../index.module.less'; import { RightCar } from './shoppingCar'; +import WorkStartDo from '../../work'; +import { TemplateType } from '@/ts/core/public/enums'; interface IProps { current: IMallTemplate; } export const MallTemplate: React.FC = ({ current }) => { + const [center, setCenter] = useState(<>); + // 加入购物车 + const onAddCar = (product: schema.XProduct, staging: boolean) => { + if (product.isMultiple) { + let num = + current.shoppingCar.products.find((i) => i.id === product.id)?.carCount || 0; + num++; + if (!num) return; + if (num > (product.count || 0)) { + message.error('库存不足'); + return; + } + current.shoppingCar.create({ + ...product, + carCount: num, + }); + message.success('已加入购物车'); + } else { + if (staging) { + current.shoppingCar.remove(product); + } else { + current.shoppingCar.create(product); + } + } + }; + // 结算 + const onPurchase = async (selectedRows: schema.XProduct[]) => { + const work = await current.findWork(); + if (!selectedRows.length) { + message.error('请先选择商品'); + return; + } + const node = await work?.loadNode(); + if (work && node) { + const rows = selectedRows.map((item) => { + return { + ...item, + orderCount: item.carCount, + }; + }); + const instance = await work.applyData(node, rows); + setCenter( + { + if (success) { + await current.shoppingCar.batchRemove(selectedRows); + } + setCenter(<>); + }} + />, + ); + } + return; + }; return ( - +
- - + + + {center}
); }; interface IHotGroup { current: IMallTemplate; + onAddCar: (product: schema.XProduct, staging: boolean) => void; + onPurchase: (selectedRows: schema.XProduct[]) => void; } -const HotBody: React.FC = ({ current }) => { +const HotBody: React.FC = ({ current, onPurchase, onAddCar }) => { const [hot, setHot] = useState(); useEffect(() => { const id = current.subscribe(async () => setHot(await current.loadHot())); @@ -67,9 +132,17 @@ const HotBody: React.FC = ({ current }) => { renderBody={(products) => { return ( - + {products.map((item) => { - return ; + return ( + + ); })} @@ -83,8 +156,12 @@ const HotBody: React.FC = ({ current }) => { interface DataProps extends IProps { form: IForm; } +interface IContentBody extends IProps { + onPurchase: (selectedRows: schema.XProduct[]) => void; + onAddCar: (product: schema.XProduct, staging: boolean) => void; +} -const ContentBody: React.FC = ({ current }) => { +const ContentBody: React.FC = ({ current, onPurchase, onAddCar }) => { const [loading, setLoading] = useState(true); const [form, setForm] = useState(); const loadContent = async () => { @@ -112,18 +189,32 @@ const ContentBody: React.FC = ({ current }) => {
- + { return ( - + {products.map((item) => { return ( - + {current.template === TemplateType.realTemplate ? ( + + ) : ( + + )} ); })} @@ -152,7 +243,11 @@ const ContentBody: React.FC = ({ current }) => { ); }; -const Group: React.FC = ({ current, form }) => { +interface IGroup extends DataProps { + onPurchase: (selectedRows: schema.XProduct[]) => void; +} + +const Group: React.FC = ({ current, form, onPurchase }) => { const [visible, setVisible] = useState(false); const [length, setLength] = useState(current.shoppingCar.products.length); useEffect(() => { @@ -176,7 +271,7 @@ const Group: React.FC = ({ current, form }) => { size={'large'} onClose={() => setVisible(false)} open={visible}> - + = ({ current, form }) => { }; const Filter: React.FC = ({ current, form }) => { - const lookups = form.fields.flatMap((item) => item.lookups ?? []); + const lookups = form.fields.flatMap((item) => + item.options?.species ? item.lookups ?? [] : [], + ); if (lookups.length === 0) { return <>; } @@ -225,15 +322,20 @@ const Filter: React.FC = ({ current, form }) => { selectByClick={true} onSelectionChanged={(e) => { const match: any = {}; - const userData: string[] = []; for (const { itemData } of e.component.getSelectedNodes()) { if (itemData?.value?.startsWith('T')) { match[itemData.value] = { _exists_: true }; } else if (itemData?.value?.startsWith('S')) { - userData.push(itemData.value); + if (match['T' + itemData.propertyId]?._in_) { + match['T' + itemData.propertyId]._in_.push(itemData.value); + } else { + match['T' + itemData.propertyId] = { + _in_: [itemData.value], + }; + } } } - current.command.emitter('filter', 'species', { match, userData }); + current.command.emitter('filter', 'species', { match }); }} />
@@ -241,7 +343,7 @@ const Filter: React.FC = ({ current, form }) => { }; interface FilterProps { - species: { userData: string[]; match: any }; + species: { match: any }; } interface ProviderProps extends DataProps { @@ -264,12 +366,11 @@ const Provider: React.FC = ({ current, form, renderBody }) => { const [size, setSize] = useState(24); const [total, setTotal] = useState(0); const filter = useRef([]); - const match = useRef({ species: { userData: [], match: {} } }); + const match = useRef({ species: { match: {} } }); const loadData = async (page: number, size: number) => { setLoading(true); const result = await form.loadThing({ requireTotalCount: true, - userData: match.current.species.userData, skip: (page - 1) * size, take: size, filter: form.parseFilter(filter.current), diff --git a/src/executor/open/mallTemplate/pages/shoppingCar.tsx b/src/executor/open/mallTemplate/pages/shoppingCar.tsx index 301dc484a008a6e9e5f42079e5be82be0d0771a5..7372717cd9a3a071486594fa9f1a79a2922eab24 100644 --- a/src/executor/open/mallTemplate/pages/shoppingCar.tsx +++ b/src/executor/open/mallTemplate/pages/shoppingCar.tsx @@ -6,13 +6,13 @@ import { Button, Divider, Skeleton, Space, Statistic } from 'antd'; import { List } from 'devextreme-react'; import React, { useEffect, useMemo, useState } from 'react'; import InfiniteScroll from 'react-infinite-scroll-component'; -import WorkStartDo from '../../work'; import { ItemProduct } from '../widget/item'; import cls from './../index.module.less'; import { IWork } from '@/ts/core'; interface IProps { page: IMallTemplate; + onPurchase: (selectedRows: schema.XProduct[]) => void; } interface IGroup { @@ -20,12 +20,11 @@ interface IGroup { items: schema.XProduct[]; } -export const RightCar: React.FC = ({ page }) => { +export const RightCar: React.FC = ({ page, onPurchase }) => { const [products, setProducts] = useState([]); const [loading, setLoading] = useState(false); const [selectedRows, setSelectedRows] = useState([]); const [work, setWork] = useState(); - const [center, setCenter] = useState(<>); const groups: IGroup[] = useMemo(() => { const result = new Link(products).GroupBy((item) => item.belongId); return Object.keys(result).map((item) => { @@ -102,7 +101,10 @@ export const RightCar: React.FC = ({ page }) => { p + (n.price ?? 0), 0)} + value={selectedRows.reduce( + (p, n) => p + ((n.price || 0) * (n.carCount || 1) ?? 0), + 0, + )} prefix={'合计:'} suffix="¥" /> @@ -122,20 +124,7 @@ export const RightCar: React.FC = ({ page }) => { type="primary" loading={loading} disabled={!work} - onClick={async () => { - const node = await work?.loadNode(); - if (work && node) { - const instance = await work.applyData(node, selectedRows); - setCenter( - setCenter(<>)} - />, - ); - } - return; - }}> + onClick={onPurchase.bind(this, selectedRows)}> {(() => { if (loading) { return '加载办事中...'; @@ -149,7 +138,6 @@ export const RightCar: React.FC = ({ page }) => {
- {center} ); }; diff --git a/src/executor/open/mallTemplate/widget/item.tsx b/src/executor/open/mallTemplate/widget/item.tsx index e6b436eb600e8b593c1f5b6e66fac81dbd6e33cf..48f8f0d529c2bd5635cde4195bc955671854deda 100644 --- a/src/executor/open/mallTemplate/widget/item.tsx +++ b/src/executor/open/mallTemplate/widget/item.tsx @@ -1,6 +1,6 @@ import { model, schema } from '@/ts/base'; import { IMallTemplate } from '@/ts/core/thing/standard/page/mallTemplate'; -import { Carousel, Image, Skeleton, Tag } from 'antd'; +import { Carousel, Image, Skeleton, Tag, InputNumber } from 'antd'; import React from 'react'; import cls from './../index.module.less'; @@ -10,32 +10,55 @@ interface IProps { images: (product: schema.XProduct) => model.FileItemShare[]; } -export const ItemProduct: React.FC = ({ product, images }) => { +export const ItemProduct: React.FC = ({ current, product, images }) => { + const onChange = (e: any) => { + const value = +e.target.value > +product.count ? product.count : e.target.value; + current.shoppingCar.create({ + ...product, + carCount: +value, + }); + }; return (
- - {images(product).map((item, index) => { - return ( - } - /> - ); - })} - -
-
-
{product.title ?? '[未设置名称]'}
- - {product.typeName ?? '商品'} - +
+ + {images(product).map((item, index) => { + return ( + } + /> + ); + })} + +
+
+
{product.title ?? '[未设置名称]'}
+ + {product.typeName ?? '商品'} + +
+
{product.remark}
+
{product.price ?? 0}¥
-
{product.remark}
-
{product.price ?? 0}¥
+ {product.isMultiple && ( +
{ + e.stopPropagation(); + }}> + +
+ )}
); }; diff --git a/src/executor/open/mallTemplate/widget/product.tsx b/src/executor/open/mallTemplate/widget/product.tsx index e2378707f3ca15c3152ebac87deb61a2f35a292b..f85b299eb895f3078b5e252c0b1d0b79b74ecd00 100644 --- a/src/executor/open/mallTemplate/widget/product.tsx +++ b/src/executor/open/mallTemplate/widget/product.tsx @@ -11,6 +11,8 @@ import DataDetails from '../components/dataDetails'; interface IProps { current: IMallTemplate; product: schema.XProduct; + onAddCar: (product: schema.XProduct, staging: boolean) => void; + onPurchase: (selectedRows: schema.XProduct[]) => void; } const images = (product: schema.XProduct): model.FileItemShare[] => { @@ -27,14 +29,16 @@ const images = (product: schema.XProduct): model.FileItemShare[] => { return images; }; -export const Product: React.FC = ({ current, product }) => { +export const Product: React.FC = ({ current, product, onAddCar, onPurchase }) => { const [center, setCenter] = useState(<>); const [staging, setStaging] = useState( - current.shoppingCar.products.some((a) => a.id == product.id), + current.shoppingCar.products.some((a) => !a.isMultiple && a.id == product.id), ); useEffect(() => { const id = current.shoppingCar.subscribe(() => { - setStaging(current.shoppingCar.products.some((a) => a.id == product.id)); + setStaging( + current.shoppingCar.products.some((a) => !a.isMultiple && a.id == product.id), + ); }); return () => current.shoppingCar.unsubscribe(id); }, []); @@ -50,74 +54,58 @@ export const Product: React.FC = ({ current, product }) => { setCenter(<>)} />, ) }> -
+ {images(product).map((item, index) => { return ( } /> ); })} - {product.title ?? '[未设置名称]'} -
+
-
{product.remark}
- {current.metadata.mode === 'sharing' && ( - <> -
- 供给方: - -
-
上架时间:{product.updateTime}
- - )} +
+ {product.brand && `[${product.brand}]`} + {product.title || '【暂无标题】'} +
+
+ 供给方: + +
+
上架时间:{product.updateTime}
- {current.metadata.mode !== 'sharing' && ( -
- ¥{product.price || 0} - {0}人已买 -
- )}
-
+
- - {current.metadata.mode !== 'sharing' ? '立即购买' : '立即申领'} - + 立即申领
-
+
{ - if (staging) { - current.shoppingCar.remove(product); - } else { - current.shoppingCar.create(product); - } - }} style={{ fontSize: 20, color: staging ? 'red' : undefined, }} /> - {current.metadata.mode === 'sharing' && ( - 加入购物车 - )} + 加入购物车
@@ -133,7 +121,9 @@ export const Product: React.FC = ({ current, product }) => { setCenter(<>)} + onPurchase={onPurchase} />, ) }> @@ -166,36 +156,27 @@ export const Product: React.FC = ({ current, product }) => {
- {current.metadata.mode !== 'sharing' && ( -
-
¥{product.price ?? 0}
-
{0}人已买
+
+
¥{product.price ?? 0}
+
+ {0}人已买{product.isMultiple && ` 数量:${product.count}件`}
- )} +
-
{}}> +
- - {current.metadata.mode !== 'sharing' ? '立即购买' : '立即申领'} - + 立即购买
{ - if (staging) { - current.shoppingCar.remove(product); - } else { - current.shoppingCar.create(product); - } - }} + onClick={onAddCar.bind(this, product, staging)} style={{ fontSize: 20, color: staging ? 'red' : undefined, }} /> - {current.metadata.mode === 'sharing' && ( - 加入购物车 - )}
diff --git a/src/executor/open/mallTemplate/widget/virtually.tsx b/src/executor/open/mallTemplate/widget/virtually.tsx new file mode 100644 index 0000000000000000000000000000000000000000..c9590a61bc213814181e3d7091c39c804578deaa --- /dev/null +++ b/src/executor/open/mallTemplate/widget/virtually.tsx @@ -0,0 +1,122 @@ +import { model, schema } from '@/ts/base'; +import { Image, Space } from 'antd'; +import React, { useEffect, useState } from 'react'; +import cls from './../index.module.less'; +import down from '/public/img/mallTemplate/down.svg'; +import { IMallTemplate } from '@/ts/core/thing/standard/page/mallTemplate'; +import EntityIcon from '@/components/Common/GlobalComps/entityIcon'; +import { ShoppingCartOutlined } from '@ant-design/icons'; +import { MallTemplateMode } from '@/ts/core/public/enums'; +import DataDetails from '../components/dataDetails'; + +interface IProps { + current: IMallTemplate; + product: schema.XProduct; + onAddCar: (product: schema.XProduct, staging: boolean) => void; + onPurchase: (selectedRows: schema.XProduct[]) => void; +} + +export const Virtually: React.FC = ({ + product, + current, + onAddCar, + onPurchase, +}) => { + const [center, setCenter] = useState(<>); + const [staging, setStaging] = useState( + current.shoppingCar.products.some((a) => !a.isMultiple && a.id == product.id), + ); + const icons: model.FileItemShare[] = JSON.parse(product.icons || '[]'); + if (icons.length == 0) { + icons.push({} as model.FileItemShare); + } + useEffect(() => { + const id = current.shoppingCar.subscribe(() => { + setStaging( + current.shoppingCar.products.some((a) => !a.isMultiple && a.id == product.id), + ); + }); + return () => current.shoppingCar.unsubscribe(id); + }, []); + return ( + <> +
+
{ + setCenter( + setCenter(<>)} + onPurchase={onPurchase} + />, + ); + }}> +
+ {icons.map((item) => { + return ( + + ); + })} + {product.title ?? '[未设置名称]'} +
+
+ +
{product.remark}
+ {product.mode === MallTemplateMode.sharing && ( + <> +
+ 供给方: + +
+
上架时间:{product.updateTime}
+ + )} +
+
+
+
+ {product.mode !== MallTemplateMode.sharing && ( +
+ ¥{product.price || 0} + {0}人已买 +
+ )} +
+
+ + + {product.mode !== MallTemplateMode.sharing ? '立即购买' : '立即申领'} + +
+
+ + {product.mode === MallTemplateMode.sharing && ( + 加入购物车 + )} +
+
+
+
+ {center} + + ); +}; diff --git a/src/executor/operate/entityForm/templateForm.tsx b/src/executor/operate/entityForm/templateForm.tsx index c1009526db45f4e8d4073cdd3531889167ff64ef..a56c4cad717314f1ea5390bdbff1cd705340da6f 100644 --- a/src/executor/operate/entityForm/templateForm.tsx +++ b/src/executor/operate/entityForm/templateForm.tsx @@ -78,8 +78,6 @@ const getMeta = (kind: string) => { const PageTemplateForm: React.FC = ({ formType, current, finished }) => { const [center, setCenter] = useState(<>); - const [isHideMode, setIsHideMode] = useState(true); - const [template, setTemplate] = useState('commonTemplate'); let initialValue: any = { public: false, open: false }; const formRef = useRef(); switch (formType) { @@ -87,11 +85,6 @@ const PageTemplateForm: React.FC = ({ formType, current, finished }) => initialValue = current.metadata; break; } - useEffect(() => { - if (formType === 'updatePageTemplate' && current.metadata.mode) { - setIsHideMode(false); - } - }, []); const columns: ProFormColumnsType[] = [ { title: '图标', @@ -101,9 +94,9 @@ const PageTemplateForm: React.FC = ({ formType, current, finished }) => return ( = ({ formType, current, finished }) => ], }, }, - { - title: '模板模式', - dataIndex: 'mode', - valueType: 'select', - initialValue: 'trading', - hideInForm: isHideMode, - fieldProps: { - options: [ - { - label: '共享', - value: 'sharing', - }, - { - label: '交易', - value: 'trading', - }, - ], - }, - }, { title: '模板', dataIndex: 'kind', @@ -244,23 +218,6 @@ const PageTemplateForm: React.FC = ({ formType, current, finished }) => generateCodeByInitials(values['name']), ); } - if ( - values['template'] === 'dataTemplate' || - values['template'] === 'realTemplate' - ) { - setIsHideMode(false); - } else if (values['template'] === 'commonTemplate') { - setIsHideMode(true); - if (Object.keys(values)[0] === 'template') { - if (values.template === 'dataTemplate') { - setTemplate('dataTemplate'); - } else if (values.template === 'realTemplate') { - setTemplate('realTemplate'); - } else { - setTemplate('commonTemplate'); - } - } - } }} onFinish={async (values) => { switch (formType) { diff --git a/src/executor/tools/task/approval/index.tsx b/src/executor/tools/task/approval/index.tsx index 6780a28a2080a018f8b94c295bb5d46ae3023ff4..7bcfe41a24851653eca24a611390910895294886 100644 --- a/src/executor/tools/task/approval/index.tsx +++ b/src/executor/tools/task/approval/index.tsx @@ -8,6 +8,7 @@ import { ReceptionChange } from '@/ts/core/work/executor/reception'; import { AddNodeType, convertToFields, getNodeByNodeId } from '@/utils/work'; import ProTable from '@ant-design/pro-table'; import { Button, Card, Input, Modal, Space, message } from 'antd'; +import { isNumber } from 'lodash'; import React, { ReactNode, useState } from 'react'; export interface TaskDetailType { @@ -112,8 +113,54 @@ const TaskApproval: React.FC = ({ task, finished, fromData }) => let content: ReactNode = null; let title = '字段变更确认'; + const calcValue = () =>{ + const instance = props.executor.task.instanceData; + if (instance) { + for (const change of props.executor.metadata.changes) { + for (const form of instance.node.forms) { + const editData: model.FormEditData[] = instance.data[change.id]; + if (change.id == form.id) { + if (editData && editData.length > 0) { + editData[editData.length - 1].after.forEach((item) => { + for (const fieldChange of change.fieldChanges) { + let afterValue = fieldChange.after + if (fieldChange.switch && typeof(afterValue)==='string') { + afterValue = afterValue && afterValue.replace(/\s/g, '') + let current = 0 + const afterValueArr: (number | string)[] = [] + const regex = /[-+*/]/; + afterValue.split('').forEach((i: string, index: number) => { + if (regex.test(i)) { + const value = item[afterValue.slice(current, index)] + afterValueArr.push(value) + current = index + 1 + afterValueArr.push(i) + } else if (index === afterValue.length - 1) { + const value = item[afterValue.slice(current, afterValue.length)] + afterValueArr.push(value) + } + }) + afterValue = eval(afterValueArr.join('')) + if (!isNumber(afterValue)) { + throw new Error(`公式${fieldChange.after}填写错误`) + } + if (!(afterValue >= 0)) { + throw new Error(`计算为负数`) + } + fieldChange.after = afterValue + fieldChange.afterName = afterValue + } + } + }) + } + } + } + } + } + } if (props.executor.metadata.funcName == '字段变更') { + calcValue() content = ( 确认后,您的数据将自动产生变更操作,变更字段如下 diff --git a/src/executor/tools/task/start/default.tsx b/src/executor/tools/task/start/default.tsx index 6f82dd9baacce24fda2a003ab4e60352f02108b6..7b8332f63fb5e4b5d717c81bca04bd9dc13d6f1c 100644 --- a/src/executor/tools/task/start/default.tsx +++ b/src/executor/tools/task/start/default.tsx @@ -69,7 +69,7 @@ const DefaultWayStart: React.FC = ({ const [ser, setSer] = useState(); const [printModal, setPrintModal] = useState(false); const service = useRef( - new WorkFormService(apply.target, apply.instanceData, true, apply.reception), + new WorkFormService(apply.target.space, apply.instanceData, true, apply.reception, undefined,apply.target), ); const hasReport = useMemo( @@ -227,6 +227,7 @@ const DefaultWayStart: React.FC = ({ removePrintIframe(); }; }, []); + return (
diff --git a/src/executor/tools/workForm/detail.tsx b/src/executor/tools/workForm/detail.tsx index 735acee196b2510080a717405c795f99be63e4af..0af6620237acdb94a6212ff2852e1fafb14185a4 100644 --- a/src/executor/tools/workForm/detail.tsx +++ b/src/executor/tools/workForm/detail.tsx @@ -21,6 +21,7 @@ import saveAs from 'file-saver'; import { IBelong } from '@/utils/excel'; import { Sequence } from '@/ts/core/thing/standard/sequence'; import { FormChangeEvent } from '@/ts/scripting/core/types/rule'; +import { ITarget } from '@/ts/core'; interface FormProps { allowEdit: boolean; form: schema.XForm; @@ -694,7 +695,7 @@ const DetailTable: React.FC = (props) => { EditModal.showFormSelect({ form: form, fields: fields, - belong: props.service.belong, + belong: props.service.target as ITarget, onSave: (values) => { values.forEach((item) => { if (formData.after.every((i) => i.id !== item.id)) { diff --git a/src/ts/base/schema.ts b/src/ts/base/schema.ts index e24a280d1286287030fff8427e5572993646ce25..93a08beb890c9a1ef3f7e2dfbfdd3dfda4d95398 100644 --- a/src/ts/base/schema.ts +++ b/src/ts/base/schema.ts @@ -1,4 +1,5 @@ import { model } from '.'; +import { MallTemplateMode, TemplateType } from '../core/public/enums'; import { ReceptionStatus, ReportTaskTreeSummary } from '../core/work/assign/reception/status'; import { NodeType, PeriodType, ReportTreeNodeTypes, ReportTreeTypes } from './enum'; @@ -889,17 +890,15 @@ export interface XPageTemplate extends XStandard { // 是否公开 open: boolean; // 模板类型 - template?: string; + template?: TemplateType; // 模板类型 kind?: string; // 自定义参数; params: T; - // 模版模式 共享 | 交易 - mode?: 'sharing' | 'trading'; } // 商城模板 -export interface XMallTemplate extends XPageTemplate {} +export interface XMallTemplate extends XPageTemplate { } // 绑定信息 export type Binding = { @@ -1290,7 +1289,7 @@ export interface XSubscription extends XSyncing { // 商品 export type XProduct = { // 交易、共享 - mode: string; + mode: MallTemplateMode; // 商城 ID mallId: string; // 商品类型 @@ -1305,6 +1304,8 @@ export type XProduct = { images?: string; // 品牌 brand?: string; + // 购买数量 + carCount?: number } & XThing; // 订单 diff --git a/src/ts/core/public/enums.ts b/src/ts/core/public/enums.ts index e20d4ad164d2a4f09a9c15f7c0ac06dfd4011a8c..5b94c70210393cf205cb21223daa2884d1cb19e4 100644 --- a/src/ts/core/public/enums.ts +++ b/src/ts/core/public/enums.ts @@ -114,3 +114,10 @@ export enum TemplateType { // 实体商品模板 realTemplate = 'realTemplate', } +/** 商城模板展示模式 */ +export enum MallTemplateMode { + // 共享 + sharing = '共享', + // 交易 + trading = '交易', +} diff --git a/src/ts/core/thing/standard/page/index.ts b/src/ts/core/thing/standard/page/index.ts index 3234a317c8738014d650e4b3f75fe3d1e7baf467..c9d36a1969f03e7e87be2827cebd372af4ab42c1 100644 --- a/src/ts/core/thing/standard/page/index.ts +++ b/src/ts/core/thing/standard/page/index.ts @@ -1,4 +1,5 @@ import { Command, schema } from '@/ts/base'; +import { TemplateType } from '@/ts/core/public/enums'; import { IWork } from '../../../work'; import { IDirectory } from '../../directory'; import { IStandardFileInfo, StandardFileInfo } from '../../fileinfo'; @@ -10,7 +11,7 @@ export interface IPageTemplate /** 自定义参数 */ params: T; /** 模板类型 */ - template?: string; + template?: TemplateType; /** 查找办事 */ loadWork(workId: string): Promise; } diff --git a/src/ts/scripting/core/services/FormServiceBase.ts b/src/ts/scripting/core/services/FormServiceBase.ts index 09f0b24ba0259c19a7fb042080c212d651e594a0..b66a703ee171676978cd88c531d2ec1cd5c992f2 100644 --- a/src/ts/scripting/core/services/FormServiceBase.ts +++ b/src/ts/scripting/core/services/FormServiceBase.ts @@ -15,6 +15,7 @@ import ValidateHandler from '../rule/ValidateHandler'; import RenderHandler from '../rule/RenderHandler'; import SplitHandler from '../rule/SplitHandler'; import { Form } from '@/ts/core/thing/standard/form'; +import { ITarget } from '@/ts/core/target/base/target' export default abstract class FormServiceBase extends ScriptEnv @@ -25,6 +26,7 @@ export default abstract class FormServiceBase readonly model: model.InstanceDataModel; readonly allowEdit: boolean; readonly reception?: XReception; + readonly target?: ITarget; readonly formCodeMap: Dictionary = {}; @@ -60,6 +62,7 @@ export default abstract class FormServiceBase allowEdit = true, reception?: XReception, functionProvider?: FunctionProvider, + target?: ITarget ) { super(functionProvider); this.belong = belong.space; @@ -67,6 +70,7 @@ export default abstract class FormServiceBase this.model = model; this.allowEdit = allowEdit; this.reception = reception; + this.target = target this.complete(this.model.node); this.executable = new ExecutableHandler(this, allowEdit);