diff --git a/public/svg/auths/authed.svg b/public/svg/auths/authed.svg new file mode 100644 index 0000000000000000000000000000000000000000..c59b1300daac5f332d0a36646084a1d3a8fad397 --- /dev/null +++ b/public/svg/auths/authed.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/svg/auths/unauth.svg b/public/svg/auths/unauth.svg new file mode 100644 index 0000000000000000000000000000000000000000..62b5181ceb0440b9fb6fe51c21f3838c85e2caf6 --- /dev/null +++ b/public/svg/auths/unauth.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/Common/GlobalComps/entityIcon.module.less b/src/components/Common/GlobalComps/entityIcon.module.less new file mode 100644 index 0000000000000000000000000000000000000000..17bcf8286cfdfdcf261e263e969bc4e9fa9aeb5a --- /dev/null +++ b/src/components/Common/GlobalComps/entityIcon.module.less @@ -0,0 +1,12 @@ +.avatar_wapper { + position: relative; +} + +.avatar_wapper .auth_icon { + position: absolute; + right: -3px; + bottom: -3px; + width: 15px; + height: 15px; + z-index: 99; +} diff --git a/src/components/Common/GlobalComps/entityIcon.tsx b/src/components/Common/GlobalComps/entityIcon.tsx index 690500c4c78c1115b112d0abc64720a1222c7ce2..f5d5e0c07fd2c1e213729a7683bc2a167e5378f1 100644 --- a/src/components/Common/GlobalComps/entityIcon.tsx +++ b/src/components/Common/GlobalComps/entityIcon.tsx @@ -7,6 +7,7 @@ import TypeIcon from './typeIcon'; import useAsyncLoad from '@/hooks/useAsyncLoad'; import { ImInfo } from 'react-icons/im'; import { IBelong } from '@/ts/core'; +import style from './entityIcon.module.less'; import { TemplateType } from '@/ts/core/public/enums'; import { isSnowflakeId } from '@/ts/base/common'; @@ -19,6 +20,7 @@ interface teamTypeInfo { title?: string; showName?: boolean; disableInfo?: boolean; + showAuthIcon?: boolean; onClick?: (entity?: schema.XEntity) => void; belong?: IBelong; showImInfo?: boolean; @@ -30,6 +32,16 @@ interface shareIconInfo extends teamTypeInfo { share?: ShareIcon; } +const renderAuthIcon = (item: any) => { + const authed = item?.authenticated; + const shouldShowIcon = + item?.share?.typeName === '人员' || item?.share?.typeName === '单位'; + const iconSrc = authed + ? `/svg/auths/authed.svg?v=1.0.0` + : `/svg/auths/unauth.svg?v=1.0.0`; + return shouldShowIcon && ; +}; + /** 实体图标 */ const EntityIcon = (info: teamTypeInfo) => { const getEntity = () => { @@ -137,7 +149,8 @@ export const ShareIconItem = (info: shareIconInfo) => { title={info.title ?? ''} onClick={() => info.onClick?.apply(this, [info.entity])}> {infoMore()} -
+
+ {renderAuthIcon(info)} { style={{ display: 'flex', cursor: 'pointer' }} onClick={() => info.onClick?.apply(this, [info.entity])}> {infoMore()} -
+
+ {renderAuthIcon(info)} {icon} {info.showName && ( diff --git a/src/components/DataPreview/session/index.tsx b/src/components/DataPreview/session/index.tsx index 81619353ada0bc9831a6fe5297ecb7a91b32b98a..572fe12bea529518b1f3f7e04f90f6de708fdd22 100644 --- a/src/components/DataPreview/session/index.tsx +++ b/src/components/DataPreview/session/index.tsx @@ -9,9 +9,11 @@ import ChatBody from './chat'; import PreviewLayout from '../layout'; import { cleanMenus } from '@/utils/tools'; import useCtrlUpdate from '@/hooks/useCtrlUpdate'; +import { PrivateInfoCard } from './settingInfo/privateInfo'; import Setting from './setting'; import useAsyncLoad from '@/hooks/useAsyncLoad'; import { Spin } from 'antd'; + const SessionBody = ({ session, relation, @@ -27,7 +29,7 @@ const SessionBody = ({ if (session.target.members.length < 30) { await session.target.loadMembers(false); } - },[session]); + }, [session]); useEffect(() => { const newActions = [ { @@ -86,39 +88,42 @@ const SessionBody = ({ } }, [target, filter]); return ( - {loaded && - { }} - contextMenu={(entity) => { - const file = (entity as IFile) || target.memberDirectory; - return { - items: cleanMenus(loadFileMenus(file)) || [], - onClick: ({ key }: { key: string }) => { - if (key === 'joinFriend') { - command.emitter('executor', key, target); - } else { - command.emitter('executor', key, file); - } - }, - }; - }} - onScrollEnd={async () => { - if (target.memberFilterCount > target.members.length) { - await target.loadMembers(false, filter); - setContent([...target.memberDirectory.content()]) - } - }} - onFilter={async (value) => { - setFilter(value); - await target.loadMembers(false, value); - setContent([...target.memberDirectory.content()]) - }} - />} + + {loaded && ( + { }} + contextMenu={(entity) => { + const file = (entity as IFile) || target.memberDirectory; + return { + items: cleanMenus(loadFileMenus(file)) || [], + onClick: ({ key }: { key: string }) => { + if (key === 'joinFriend') { + command.emitter('executor', key, target); + } else { + command.emitter('executor', key, file); + } + }, + }; + }} + onScrollEnd={async () => { + if (target.memberFilterCount > target.members.length) { + await target.loadMembers(false, filter); + setContent([...target.memberDirectory.content()]); + } + }} + onFilter={async (value) => { + setFilter(value); + await target.loadMembers(false, value); + setContent([...target.memberDirectory.content()]); + }} + /> + )} + ); }; @@ -133,7 +138,24 @@ const SessionBody = ({ case 'relation': return ; case 'setting': { - return ; + return ( + <> + {relation ? ( + + ) : ( + <> + )} + + + ); } default: return <>; diff --git a/src/components/DataPreview/session/settingInfo/index.tsx b/src/components/DataPreview/session/settingInfo/index.tsx deleted file mode 100644 index 1edc011012b4c3decf98467f56608d9b7a69206b..0000000000000000000000000000000000000000 --- a/src/components/DataPreview/session/settingInfo/index.tsx +++ /dev/null @@ -1,438 +0,0 @@ -import React, { useRef, useState } from 'react'; -import orgCtrl from '@/ts/controller'; -import WorkFormViewer from '@/components/DataStandard/WorkForm/Viewer'; -import OpenFileDialog from '@/components/OpenFileDialog'; -import { ISession, IForm, ITarget, IFile, XCollection } from '@/ts/core'; -import { Button, Card } from 'antd'; -import { Xbase } from '@/ts/base/schema'; -import useAsyncLoad from '@/hooks/useAsyncLoad'; -import { Form } from '@/ts/core/thing/standard/form'; -import FullScreenModal from '@/components/Common/fullScreen'; -import { DataResource } from '@/ts/core/thing/resource'; -import FormService from '@/ts/scripting/core/services/FormService'; - -export type InfoID = { - // 信息名称 - InfoTypeName: string; - // 信息的表单 - InfoFormID: string; -} & Xbase; - -interface ICardProps { - //权限 - auth: Array; - //信息名称 - title: string; - //信息存储的空间 - space: ITarget; - //当前查看的对象 - target?: ITarget; -} - -export type IContentProps = { - InfoTypeName: string; - targetID: string; -} & Xbase & { [key: string]: any }; -// 用于创建自定义集合 -const collName = '-resource-info'; -const contentCollName = '-resource-content'; - -interface ISettingInfoEntity { - space: ITarget; - coll: XCollection; - title: string; - form: IForm | undefined; - contentColl: XCollection | undefined; - content: IContentProps | undefined; -} -//设置信息实体 -class SettingInfoEntity implements ISettingInfoEntity { - space: ITarget; - coll: XCollection; - title: string; - form: IForm | undefined; - contentColl: XCollection | undefined; - content: IContentProps | undefined; - infoId: InfoID | undefined; - changeValue: any; - resource: DataResource; - constructor(space: ITarget, title: string) { - this.space = space; - - let resource = this.space.resource; - if (!resource) { - resource = new DataResource(this.space.metadata, [], [this.space.metadata.id]); - } - this.resource = resource; - this.coll = resource.genColl(collName); - this.contentColl = resource.genColl(contentCollName); - this.title = title; - } - - async loadForm(): Promise { - const res = await this.coll.load({}); - const targetInfoId = res.findLast((infoId) => { - return infoId.InfoTypeName == this.title; - }); - - if (targetInfoId) { - this.infoId = targetInfoId; - - const formArr = await this.resource.formColl.find([targetInfoId?.InfoFormID]); - if (formArr[0]) { - const formInst = new Form( - { ...formArr[0], id: formArr[0].id + '_' }, - this.space.directory, - ); - await formInst.loadFields(); - this.form = formInst; - this.contentColl = this.resource.genColl( - this.form.metadata.collName || '_system-things', - ); - } - } - } - - async changeForm(form: IForm): Promise { - if (this.infoId) { - this.infoId.InfoFormID = form.id; - await this.coll.update(this.infoId.id, { - _set_: { - InfoTypeName: this.infoId.InfoTypeName, - InfoFormID: this.infoId.InfoFormID, - }, - }); - } else { - const data = { - InfoTypeName: this.title, - InfoFormID: form.id, - }; - await this.coll.insert(data as unknown as InfoID); - } - } - - async loadInfo(target: ITarget): Promise { - // 使用新的集合去存储表单的数据 - if (this.contentColl) { - const res = await this.contentColl.load({}); - const typeContentArr = res.filter((data) => { - return data.InfoTypeName == this.title; - }); - const content = typeContentArr.find((data) => { - return data.targetID === target.id; - }); - if (!content) { - const newContent = { - InfoTypeName: this.title, - targetID: target.id, - labels: ['F' + this.form?.id], - name: this.form?.name, - }; - this.content = await this.contentColl.insert( - newContent as unknown as IContentProps, - ); - } else { - this.content = content; - } - return this.getContentValue(); - } - } - - getContentValue() { - if (this.content) { - const data: { [key: string]: any } = {}; - if (this.form?.fields) { - this.form.fields.forEach((fieldId) => { - if (this.content![fieldId.code]) { - data[fieldId.id] = this.content![fieldId.code]; - } - }); - } - return data; - } - } - - setContentValue() {} - - changeContent(data: any) { - this.changeValue = data; - } - - async saveInfo(): Promise { - if (this.content) { - const saveData: { [key: string]: any } = {}; - for (let key in this.changeValue) { - if (this.changeValue[key] && key !== 'name') { - saveData['T' + key] = this.changeValue[key]; - } - } - if (this.contentColl) { - await this.contentColl.update(this.content?.id, { - _set_: { ...saveData }, - }); - } - } - } -} - -const InfoCard: React.FC = (props) => { - const { space, target, title } = props; - // 表单选择器开关 - const [needType, setNeedType] = useState(''); - // 选定的表单 - const [form, setForm] = useState(undefined); - - const [formData, setFormData] = useState(undefined); - const [infoEntity] = useState(new SettingInfoEntity(space, title)); - const server = useRef(); - const setAuth = props.auth.indexOf('set') > -1; - const editAuth = props.auth.indexOf('edit') > -1; - const previewAuth = props.auth.indexOf('preview') > -1; - // 使用useAsyncLoad 加载表单的信息 - useAsyncLoad(async () => { - await loadForm(); - if (previewAuth) { - await loadInfo(); - } - }); - - // 加载信息绑定的表单 - const loadForm = async () => { - // await infoEntity.loadForm(); - // setForm(infoEntity.form); - }; - - // 加载信息 - const loadInfo = async () => { - // setFormData(await infoEntity.loadInfo(target || space)); - }; - // 设置信息 - const setInfo = (fieldId: string, value: any, data: any) => { - // infoEntity.changeContent(data); - }; - // 保存信息 - const saveInfo = async () => { - // await infoEntity.saveInfo(); - }; - - const getButtons = () => { - const bottonArr = []; - if (setAuth) - bottonArr.push( - , - ); - return bottonArr; - }; - const getContent = () => { - //根据权限 数据 当前选择切换展示的信息 - return ( - <> - - - ); - }; - const onSelectFinish = async (files: IFile[]) => { - if ('表单' == needType) { - const form = files[0] as IForm; - await form.loadFields(); - setForm(form); - server.current = FormService.fromIForm(form); - // infoEntity.changeForm(form); - } - setNeedType(''); - }; - return ( - <> - - {getContent()} - - - {form && server.current && ( - { - setNeedType(''); - }}> - - - )} - - {needType == '表单' && ( - setNeedType('')} - onOk={onSelectFinish} - /> - )} - - ); -}; - -interface IProps { - target: ITarget; - session: ISession; -} - -const SettingInfoArr: React.FC = (props) => { - const { target, session } = props; - // 本人对象 - const mySelf = orgCtrl.user; - - // - const arr = []; - // 群权限 - const hasRelationAuth = session.target.hasRelationAuth(); - // 基本信息-公开信息 - // const reloadFinish = () => { - // orgCtrl.changCallback(); - // }; - // arr.push( - // - // {/* */} - // , - // ); - - // 基本信息-备注信息 - let lastItemAuth = ['edit', 'preview']; - - // 对象类型 - switch (target.typeName) { - case '人员': - if (session.typeName == '人员') { - if (target.metadata.id === orgCtrl.user.id) { - // 本人 - lastItemAuth = ['set']; - arr.push( - , - ); - arr.push( - , - ); - // arr.push(); - } else { - // 好友 - arr.push( - , - ); - arr.push( - , - ); - } - } else if (session.typeName == '单位') { - // 同事 - arr.push( - , - ); - if (hasRelationAuth) - arr.push( - , - ); - } - break; - case '单位': - if (hasRelationAuth) { - arr.push( - , - ); - arr.push( - , - ); - arr.push( - , - ); - } else { - arr.push( - , - ); - arr.push( - , - ); - arr.push( - , - ); - } - break; - default: - break; - } - const lastItem = ( - - ); - arr.push(lastItem); - - return <>{arr}; -}; - -export default SettingInfoArr; diff --git a/src/components/DataPreview/session/settingInfo/privateInfo/PrivateInfo.ts b/src/components/DataPreview/session/settingInfo/privateInfo/PrivateInfo.ts new file mode 100644 index 0000000000000000000000000000000000000000..50485c8841e746c25df346cb424016ff7846c4ce --- /dev/null +++ b/src/components/DataPreview/session/settingInfo/privateInfo/PrivateInfo.ts @@ -0,0 +1,540 @@ +import { ITarget, XCollection } from '@/ts/core'; +import { DataResource } from '@/ts/core/thing/resource'; +import { XTarget, Xbase } from '@/ts/base/schema'; +import { + personApplyFields, + organizationApplyFields, + personFields, + organizationFields, +} from './fixFormField'; +import { LoadOptions } from '@/ts/base/model'; +import { model } from '@/ts/base'; + +const personKeys = ['name', 'phoneNumber', 'identificationNumber']; +const organizationKeys = [ + 'organizationName', + 'orgldentificationType', + 'orgldentificationNumber', + 'name', + 'identificationType', + 'identificationNumber', +]; + +const collName = '-resource-privateinfomation'; +type information = Xbase & { [key: string]: any }; + +/** + * 信息管理 + */ +class BaseInfoManager { + /** + * 自定义的集合 用于存储所有相关数据 + */ + private coll: XCollection; + /** + * 缓存的所有数据 + */ + private res?: information[]; + // private space: DataResource; + constructor(space: DataResource) { + this.coll = space.genColl(collName); + // this.space = space; + } + + /** + * 加载信息并选择是否过滤掉已删除的数据 + * @param options 过滤的规则 + * @returns + */ + private async getInfoByOptions(options: LoadOptions, filtDeleted = true) { + return (await this.coll.load({ options })).filter( + (item) => item.isDeleted != filtDeleted, + ); + } + + /** + * 获得单类型的数据 + * @param infoType 信息类型 PrivateInfo JoinInfo joinApplyInfo + */ + async getInfoByInfoType(infoType: string) { + const options = { + match: { + infoType, + }, + } as LoadOptions; + return this.getInfoByOptions(options); + } + /** + * 获得单类型的最后一条 + * @param infoType 信息类型 PrivateInfo JoinInfo + * @returns + */ + async getLastInfoByInfoType(infoType: string) { + const res = await this.getInfoByInfoType(infoType); + if (res.length > 0) { + return res[res.length - 1]; + } + } + + /** + * 获得多类型类型的数据 + * @param infoTypes 类型组 + */ + async getInfoByInfoTypes(infoTypes: string[]) { + const options = { + match: { + infoType: { + _in_: infoTypes, + }, + }, + } as LoadOptions; + + return this.getInfoByOptions(options); + } + + /** + * @param infoType 信息的类型 + * @param data 存储的数据 + * @returns + */ + async createInfo(infoType: string, data: { [key: string]: any }) { + data['infoType'] = infoType; + let res; + try { + res = await this.coll.insert(data as unknown as information); + } catch (e) { + console.log(e); + } + + return res; + } + /** + * + * @param id 信息的id + * @param data 要更新的部分 + * @returns + */ + async upDate(id: string, data: { [key: string]: any }) { + const res = await this.coll.update(id, data); + return res; + } + + /** + * 测试方法用于删除开发中产生的数据 + */ + async deleltAllInfo() { + if (this.res) { + const res = await this.coll.deleteMany(this.res); + console.log('deleteAllInfo:', res); + } + } +} +/** + *设置页面个人单位隐私信息管理器 + */ +export class SettingInfoManager { + /** + * 信息管理器 + */ + private infoManager: BaseInfoManager; + /** + * 信息存储的空间 + */ + private space: DataResource; + + /** + * 是否有写入的权限 + */ + private auth; + + public privateInfoFields: model.FieldModel[]; + public joinInfoFields: model.FieldModel[]; + + /** + * 个人、单位隐私的信息 + */ + public privateInfo?: information; + /** + * 加入的要求填写的信息 + */ + public joinInfo?: information; + + constructor(space: DataResource, auth: boolean) { + this.infoManager = new BaseInfoManager(space); + this.space = space; + this.auth = auth; + const sTarget = this.space.targetMetadata; + this.privateInfoFields = + sTarget.typeName === '人员' ? personFields : organizationFields; + this.joinInfoFields = personApplyFields; + if (['组织群'].indexOf(sTarget.typeName) > -1) { + this.joinInfoFields = organizationApplyFields; + } + } + /** + * 初始化信息 加载基础的两个数据 + */ + + async init() { + await this.getPrivateInfo(); + await this.getJoinInfo(); + } + + // 获取该空间下的隐私信息 如果不为 人员 或者单位则返回空 + private async getPrivateInfo() { + let res = await this.infoManager.getLastInfoByInfoType('PrivateInfo'); + if (!res && this.auth) { + // 当类型为人员、单位时根据系统信息创建信息 + const newData = this.getSystemData(); + switch (this.space.targetMetadata.typeName) { + case '人员': + res = await this.infoManager.createInfo('PrivateInfo', newData); + break; + case '单位': + res = await this.infoManager.createInfo('PrivateInfo', newData); + break; + } + } + this.privateInfo = res; + return this.privateInfo; + } + // 获取系统的信息 + private getSystemData() { + const sTarget = this.space.targetMetadata; + const data: { [key: string]: any } = {}; + switch (sTarget.typeName) { + case '人员': + data['name'] = sTarget.name; + data['phoneNumber'] = sTarget.code; + break; + case '单位': + data['organizationName'] = sTarget.name; + data['orgldentificationNumber'] = sTarget.code; + break; + default: + } + return data; + } + // 获取该空间下的申请设定信息 + private async getJoinInfo() { + let res = await this.infoManager.getLastInfoByInfoType('JoinInfo'); + if (!res && this.auth) { + // 根据类型不同创建不同的初始数据 + const data: { [key: string]: any } = {}; + this.joinInfoFields.forEach((field) => { + data[field.code] = false; + }); + res = await this.infoManager.createInfo('JoinInfo', data); + } + + this.joinInfo = res; + return this.joinInfo; + } + + /** + * 更新集合下信息 + * @param infoType 类型有:PrivateInfo JoinInfo joinApplyInfo memberInfo + */ + async updated(infoType: String, data?: { [key: string]: any }) { + switch (infoType) { + case 'PrivateInfo': + if (this.privateInfo) { + await this.infoManager.upDate(this.privateInfo.id, this.privateInfo); + } + break; + case 'JoinInfo': + if (this.joinInfo) await this.infoManager.upDate(this.joinInfo.id, this.joinInfo); + break; + case 'memberInfo': + if (data) await this.infoManager.upDate(data.id, data); + break; + } + } + + /** + * 保存用户加入其他群时填写的信息 + * @param applicant 申请人 + * @param target 被申请人 + * @param data 数据 + */ + async saveApllyInfo(applicant: XTarget, target: XTarget, data: { [key: string]: any }) { + if (data) { + data['applicantId'] = applicant.id; + data['targetId'] = target.id; + } + await this.infoManager.createInfo('joinApplyInfo', data); + } + + /** + * 读取用户加入其他群时填写的信息 + * @param target 被申请人 + */ + async loadApplyInfo(target: XTarget) { + // TODO 如果加了多次如何与办事匹配上 + const res = await this.infoManager.getInfoByInfoType('joinApplyInfo'); + return res.findLast((info) => { + return info['targetId'] == target.id; + }); + } + + /** + * 当通过加入审批时把信息存在集合中并设置类型为 memberInfo + * @param data + */ + async saveMemberInfo(data: { [key: string]: any }) { + // 传入的信息过滤下 + console.log('存下申请人信息'); + + const fields = this.getMemberInfoFileds(); + const saveData: { [key: string]: any } = {}; + fields?.forEach((field) => { + saveData[field.code] = data[field.code]; + }); + saveData['applicantId'] = data['applicantId']; + saveData['targetId'] = data['targetId']; + const res = await this.infoManager.createInfo('memberInfo', saveData); + console.log(res); + } + + async loadMemberInfo(member: XTarget) { + const res = await this.infoManager.getInfoByInfoType('memberInfo'); + return res.findLast((info) => info['applicantId'] == member.id); + } + + /** + * 获取成员信息的固定属性字段 + */ + getMemberInfoFileds() { + const sTarget = this.space.targetMetadata; + let allFields = personFields; + if (['组织群'].indexOf(sTarget.typeName) > -1) { + allFields = organizationFields; + } + + const fields = allFields.filter((field) => { + if (this.joinInfo) return this.joinInfo[field.code + '_isNeed']; + }); + return fields; + } + + async deleltAllInfo() { + await this.infoManager.deleltAllInfo(); + } +} + +/** + * 用于自定义信息的增改 + */ +export class PrivateInfoManager { + private target: ITarget; + public coll: XCollection; + // 自身隐私数据 + public data: { [key: string]: any }; + private keys: Array; + // 其他加入设定数据 + public joinData: { [key: string]: string }; + private joinKeys: Array; + + // 该数据集合 + private res?: information[]; + constructor(target: ITarget, space?: DataResource) { + this.target = target; + const resource = + space || + this.target.resource || + new DataResource(this.target.metadata, [], [this.target.metadata.id]); + this.coll = resource.genColl(collName); + this.data = {}; + this.joinData = {}; + switch (target.typeName) { + case '人员': + this.keys = personKeys; + this.joinKeys = personKeys; + break; + case '单位': + this.keys = organizationKeys; + this.joinKeys = personKeys; + break; + default: + this.keys = []; + this.joinKeys = []; + } + } + + async getInfoByKeys() { + let data = await this.getInfoByKey('PrivateInfo'); + if (data) { + this.data = data; + this.checkData(); + } + data = await this.getInfoByKey('JoinInfo'); + if (data) { + this.joinData = data; + } + } + + async getInfoByKey(infoType: string) { + if (!this.res) this.res = await this.coll.load({}); + const res = this.res; + const target = res.find((item) => { + return item.infoType && item.infoType == infoType; + }); + if (target) { + return target; + } else { + const newInfo = await this.createInfoBykey(infoType); + return newInfo; + } + } + + async createInfoBykey(infoType: string) { + const keys = infoType == 'PrivateInfo' ? this.keys : this.joinKeys; + const data: { [key: string]: any } = {}; + keys.forEach((key) => { + data[key] = ''; + if (infoType == 'JoinInfo') { + data[key] = 'false'; + } + }); + data['infoType'] = infoType; + const res = await this.coll.insert(data as unknown as information); + return res; + } + + async changeByKey(infoType: String) { + console.log('更新信息'); + const data = infoType == 'PrivateInfo' ? this.data : this.joinData; + if (data) { + const res = await this.coll.update(data.id, { + _set_: { ...data }, + }); + console.log(res); + } + } + // 自动拉拉取数据 + checkData() { + switch (this.target.typeName) { + case '人员': + console.log(this.data['name'], this.data['name'] == ''); + if (this.data['name'] == '') { + this.data['name'] = this.target.name; + } + if (this.data['phoneNumber'] == '') { + this.data['phoneNumber'] = this.target.code; + } + break; + case '单位': + if (this.data['organizationName'] == '') { + this.data['organizationName'] = this.target.name; + } + if (this.data['orgldentificationNumber'] === '') { + this.data['orgldentificationNumber'] = this.target.code; + } + break; + default: + this.keys = []; + } + } + + getMemberInfo(target: XTarget) { + if (this.res) { + const memberInfo = this.res.findLast((item) => { + return ( + item.infoType && item.infoType == 'memberInfo' && item.applicantId == target.id + ); + }); + return memberInfo; + } + } +} + +export class JoinInfoManager { + public PIManager: PrivateInfoManager; + public data: { [key: string]: any }; + constructor(space: ITarget, target: XTarget) { + console.log(space); + // const resource = new DataResource(space, [], [space.id]); + this.PIManager = new PrivateInfoManager(space); + this.data = { + targetId: target.id, + applicantId: space.id, + infoType: 'joinApplyInfo', + }; + } + + setData(key: string, value: any) { + this.data[key] = value; + } + + async saveData(data?: any) { + if (data) { + data['targetId'] = this.data['targetId']; + data['applicantId'] = this.data['applicantId']; + data['infoType'] = this.data['infoType']; + } + console.log(data); + + await this.PIManager.coll.insert(data || (this.data as unknown as information)); + } +} + +export class JoinInfoReader { + private PIManager: PrivateInfoManager; + /** 被申请者*/ + public target: XTarget; + /** 申请者*/ + private space: XTarget; + private TPIManager: PrivateInfoManager; + public applyData?: { [key: string]: string }; + public requreData?: { [key: string]: string }; + + constructor(space: XTarget, target: XTarget) { + const resource = new DataResource(space, [], [space.id]); + this.PIManager = new PrivateInfoManager(space as any, resource); + const tResource = new DataResource(target, [], [target.id]); + this.TPIManager = new PrivateInfoManager(tResource as any, tResource); + this.target = target; + this.space = space; + } + + /** + * 在申请者空间下找到申请时填写的信息 + * 在被申请者空间下找到要求填写的信息 + * @returns 返回找的申请时填写的信息 + */ + async getInfo() { + await this.PIManager.getInfoByKeys(); + await this.TPIManager.getInfoByKeys(); + const res = await this.PIManager.coll.load({}); + const target = res.findLast((item) => { + return ( + item.infoType && + item.infoType == 'joinApplyInfo' && + item.targetId == this.target.id && + item.applicantId == this.space.id + ); + }); + this.applyData = target; + this.requreData = this.TPIManager.joinData; + + return target; + } + + // 审批通过时记录该信息 + async saveInfo() { + // return this.TPIManager.coll.insert(this.target); + console.log(this.applyData); + const data: { [key: string]: string } = {}; + if (this.applyData) { + for (let key in this.requreData) { + if (this.requreData[key] === 'true') data[key] = this.applyData[key]; + } + data['targetId'] = this.applyData['targetId']; + data['applicantId'] = this.applyData['applicantId']; + data['infoType'] = 'memberInfo'; + console.log(data); + + return this.TPIManager.coll.insert(data as any); + } + } +} diff --git a/src/components/DataPreview/session/settingInfo/privateInfo/fixFormField.ts b/src/components/DataPreview/session/settingInfo/privateInfo/fixFormField.ts new file mode 100644 index 0000000000000000000000000000000000000000..1be0fbd4433417486ac7ede00243e2773508e92e --- /dev/null +++ b/src/components/DataPreview/session/settingInfo/privateInfo/fixFormField.ts @@ -0,0 +1,191 @@ +import { model } from '@/ts/base'; +export const personFields: model.FieldModel[] = [ + { + id: 'name', + name: '姓名', + code: 'name', + valueType: '描述型', + remark: '', + }, + { + id: 'phoneNumber', + name: '手机号', + code: 'phoneNumber', + valueType: '描述型', + remark: '', + }, + { + id: 'identificationNumber', + name: '身份证', + code: 'identificationNumber', + valueType: '描述型', + remark: '', + }, +]; + +export const organizationFields: model.FieldModel[] = [ + { + id: 'organizationName', + name: '企业名称', + code: 'organizationName', + valueType: '描述型', + remark: '', + }, + { + id: 'orgldentificationType', + name: '企业识别号类型', + code: 'orgldentificationType', + valueType: '选择型', + remark: '', + lookups: [ + { + id: '', + code: '', + text: '企业注册号', + value: '10', + info: '', + }, + { + id: '', + code: '', + text: '统一社会信用代码', + value: '20', + info: '', + }, + ], + }, + { + id: 'orgldentificationNumber', + name: '企业识别号码', + code: 'orgldentificationNumber', + valueType: '描述型', + remark: '', + }, + { + id: 'name', + name: '法人姓名', + code: 'name', + valueType: '描述型', + remark: '', + }, + { + id: 'identificationType', + name: '法人证件类型', + code: 'identificationType', + valueType: '选择型', + remark: '', + lookups: [{ id: '', code: '', value: '0', text: '身份证', info: '' }], + }, + { + id: 'identificationNumber', + name: '法人身份证件号码', + code: 'identificationNumber', + valueType: '描述型', + remark: '', + }, +]; + +const chooseLookUp = [ + { + id: '', + code: '', + text: '要求填写', + value: true, + info: '', + }, + { + id: '', + code: '', + text: '不要求填写', + value: false, + info: '', + }, +]; + +export const personApplyFields: model.FieldModel[] = [ + { + id: 'name_isNeed', + name: '姓名', + code: 'name_isNeed', + valueType: '描述型', + widget: '单选框', + lookups: chooseLookUp, + remark: '', + }, + { + id: 'phoneNumber_isNeed', + name: '手机号', + code: 'phoneNumber_isNeed', + valueType: '描述型', + widget: '单选框', + lookups: chooseLookUp, + remark: '', + }, + { + id: 'identificationNumber_isNeed', + name: '身份证', + code: 'identificationNumber_isNeed', + valueType: '描述型', + widget: '单选框', + lookups: chooseLookUp, + remark: '', + }, +]; + +export const organizationApplyFields: model.FieldModel[] = [ + { + id: 'organizationName_isNeed', + name: '企业名称', + code: 'organizationName_isNeed', + valueType: '描述型', + widget: '单选框', + lookups: chooseLookUp, + remark: '', + }, + { + id: 'orgldentificationType_isNeed', + name: '企业识别号类型', + code: 'orgldentificationType_isNeed', + valueType: '选择型', + widget: '单选框', + + remark: '', + lookups: chooseLookUp, + }, + { + id: 'orgldentificationNumber_isNeed', + name: '企业识别号码', + code: 'orgldentificationNumber_isNeed', + valueType: '描述型', + widget: '单选框', + lookups: chooseLookUp, + remark: '', + }, + { + id: 'name_isNeed', + name: '法人姓名', + code: 'name_isNeed', + valueType: '描述型', + widget: '单选框', + lookups: chooseLookUp, + remark: '', + }, + { + id: 'identificationType_isNeed', + name: '法人证件类型', + code: 'identificationType_isNeed', + valueType: '选择型', + widget: '单选框', + lookups: chooseLookUp, + remark: '', + }, + { + id: 'identificationNumber_isNeed', + name: '法人身份证件号码', + code: 'identificationNumber_isNeed', + valueType: '描述型', + widget: '单选框', + lookups: chooseLookUp, + remark: '', + }, +]; diff --git a/src/components/DataPreview/session/settingInfo/privateInfo/fixedForm.tsx b/src/components/DataPreview/session/settingInfo/privateInfo/fixedForm.tsx new file mode 100644 index 0000000000000000000000000000000000000000..f0666ddc99df47158b2743f6c179c756012a4ec4 --- /dev/null +++ b/src/components/DataPreview/session/settingInfo/privateInfo/fixedForm.tsx @@ -0,0 +1,94 @@ +import FormItem from '@/components/DataStandard/WorkForm/Viewer/formItem'; +import { model } from '@/ts/base'; +import Toolbar, { Item } from 'devextreme-react/toolbar'; +import React from 'react'; +import { getItemNums } from '@/components/DataStandard/WorkForm/Utils'; +import useStorage from '@/hooks/useStorage'; +import { IBelong, ITarget } from '@/ts/core'; + +interface IProps { + data: any; + fields: model.FieldModel[]; + + /** + * 信息的归属方 + */ + space: ITarget; + onValuesChange?: (field: string, value: any) => void; + onSetButtonClick?: () => void; + onSyncButtonClick?: () => void; + readOnly?: boolean; + //todo 加入类似插槽的 允许外部传入按钮 实现功能组合 例如同步隐私信息 同步系统信息等 +} + +/** + * 固定属性的表单数据展示 + */ +export const FixedForm: React.FC = (props) => { + const [colNum, setColNum] = useStorage('workFormColNum', '一列'); + return ( + <> + + {props.onSyncButtonClick && ( + + )} + + {props.onSetButtonClick && ( + + )} + { + setColNum(e.itemData); + }, + }} + /> + +
+ {props.fields.map((field) => { + // if (field.widget === '单选框') { + // console.log(11111); + + // return ; + // } + + return ( + + ); + })} +
+ + ); +}; diff --git a/src/components/DataPreview/session/settingInfo/privateInfo/fixedFormItem.tsx b/src/components/DataPreview/session/settingInfo/privateInfo/fixedFormItem.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5ecfca83e210fde0fad29a0aee5c31c1255e903c --- /dev/null +++ b/src/components/DataPreview/session/settingInfo/privateInfo/fixedFormItem.tsx @@ -0,0 +1,225 @@ +// import React, { useEffect, useState, createRef, useCallback } from 'react'; +import React from 'react'; +import { getWidget, getItemWidth } from '@/components/DataStandard/WorkForm/Utils'; +import { + // DropDownBox, + // DateBox, + // NumberBox, + SelectBox, + // TextArea, + TextBox, + // TreeView, +} from 'devextreme-react'; +import { model } from '@/ts/base'; + +export type FAttribute = { + // 名称 + name: string; + // 编号 + code: string; + // 特性显示组件 + widget?: string; + // 用于校验的属性 + rule?: string; + lookups?: FFiledLookup[]; +}; + +export type FFiledLookup = { + /** 分类项值 */ + value: string; + /** 描述(项名称) */ + text: string; +}; + +export const personAttributes: FAttribute[] = [ + { + name: '姓名', + code: 'name', + widget: '描述型', + }, + { + name: '手机号', + code: 'phoneNumber', + widget: '描述型', + }, + { + name: '身份证', + code: 'identificationNumber', + widget: '描述型', + }, +]; + +export const personFields: model.FieldModel[] = [ + { + id: 'name', + name: '姓名', + code: 'name', + valueType: '描述型', + remark: '', + }, + { + id: 'phoneNumber', + name: '手机号', + code: 'phoneNumber', + valueType: '描述型', + remark: '', + }, + { + id: 'identificationNumber', + name: '身份证', + code: 'identificationNumber', + valueType: '描述型', + remark: '', + }, +]; + +export const organizationAttributes: FAttribute[] = [ + { + name: '企业名称', + code: 'organizationName', + widget: '描述型', + }, + { + name: '企业识别号类型', + code: 'orgldentificationType', + widget: '选择型', + // lookups: ['企业注册号', '统一社会信用代码'], + lookups: [ + { value: '10', text: '企业注册号' }, + { value: '20', text: '统一社会信用代码' }, + ], + }, + { + name: '企业识别号码', + code: 'orgldentificationNumber', + widget: '描述型', + }, + { + name: '法人姓名', + code: 'name', + widget: '描述型', + }, + { + name: '法人证件类型', + code: 'identificationType', + widget: '选择型', + lookups: [{ value: '0', text: '身份证' }], + }, + { + name: '法人身份证件号码', + code: 'identificationNumber', + widget: '描述型', + }, +]; + +export const organizationFields: model.FieldModel[] = [ + { + id: 'organizationName', + name: '企业名称', + code: 'organizationName', + valueType: '描述型', + remark: '', + }, + { + id: 'orgldentificationType', + name: '企业识别号类型', + code: 'orgldentificationType', + valueType: '选择型', + remark: '', + lookups: [ + { + id: '', + code: '', + text: '企业注册号', + value: '10', + info: '', + }, + { + id: '', + code: '', + text: '统一社会信用代码', + value: '20', + info: '', + }, + ], + }, + { + id: 'orgldentificationNumber', + name: '企业识别号', + code: 'orgldentificationNumber', + valueType: '描述型', + remark: '', + }, + { + id: 'name', + name: '法人姓名', + code: 'name', + valueType: '描述型', + remark: '', + }, + { + id: 'identificationType', + name: '法人证件类型', + code: 'identificationType', + valueType: '选择型', + remark: '', + lookups: [{ id: '', code: '', value: '0', text: '身份证', info: '' }], + }, + { + id: 'identificationNumber', + name: '法人身份证件号码', + code: 'identificationNumber', + valueType: '描述型', + remark: '', + }, +]; + +interface Iprops { + attr: FAttribute; + defaultValue: any; + value?: any; + onValueChange: (value: any) => {}; +} + +const mixOptions: any = { + height: 36, + showClearButton: true, + width: getItemWidth('二列'), + showMaskMode: 'always', + labelMode: 'floating', + labelLocation: 'left', +}; + +export const FixedFormItem: React.FC = ({ + attr, + defaultValue, + value, + onValueChange, +}) => { + switch (getWidget(attr.widget)) { + case '文本框': + return ( + + ); + case '单选框': + return ( + onValueChange(value)} + {...mixOptions} + /> + ); + } +}; diff --git a/src/components/DataPreview/session/settingInfo/privateInfo/index.tsx b/src/components/DataPreview/session/settingInfo/privateInfo/index.tsx new file mode 100644 index 0000000000000000000000000000000000000000..59a27103c1c27f5715065cba0b4ba1038910da07 --- /dev/null +++ b/src/components/DataPreview/session/settingInfo/privateInfo/index.tsx @@ -0,0 +1,265 @@ +import React, { useState } from 'react'; +import { useAsync } from 'react-use'; +import { kernel } from '@/ts/base'; +import QrCode from 'qrcode.react'; +import { ISession, ITarget } from '@/ts/core'; +import { Button, Card, message } from 'antd'; +import FullScreenModal from '@/components/Common/fullScreen'; +import { SettingInfoManager } from './PrivateInfo'; +import { FixedForm } from './fixedForm'; +import orgCtrl from '@/ts/controller'; +interface Iprops { + /** + * 发生的会话 + */ + session: ISession; + /** + * 在谁的空间下 + */ + space: ITarget; + /** + * 查看谁 + */ + target: ITarget; +} + +// 通过 HTTP 请求发送认证相关数据 +function sendAuthData( + url: string, + data: Record, +): Promise> { + return new Promise((ok, error) => { + const xhr = new XMLHttpRequest(); + xhr.open('POST', url); + xhr.setRequestHeader('content-type', 'application/json'); + xhr.onreadystatechange = function () { + if (xhr.readyState === XMLHttpRequest.DONE) { + if (xhr.status === 200) { + // 获取响应数据的原始文本内容 + const rawText = xhr.responseText; + try { + ok(JSON.parse(rawText)); + } catch (e) { + error('请求失败'); + } + } else { + error('请求失败'); + } + } + }; + xhr.send(JSON.stringify(data)); + }); +} + +function zlbFaceAuth(requestId: string, setQrcode: (url: string) => void): Promise { + const accessToken = kernel.accessToken; + return new Promise((ok, error) => { + const zlbInstance = new window._GovAuthentication({ + accessKey: 'BCDSGA_c5c86d2c327e2a5d454dba9ab0fd718c', + requestId, + onlnited: function (url: string) { + setQrcode(url); + }, + onVerifyResult: async function (result: Record) { + setQrcode(''); + + if (!result.pass) { + return error(result.msg); + } + + // 认证成功 + const verifyResult = await sendAuthData('/ZLB/Verify', { + RequestId: requestId, + AccessToken: accessToken, + }); + + if (!verifyResult.success) { + return error(verifyResult.message); + } + + ok(); + // console.log( + // await sendAuthData('/ZLB/IsVerify', { + // AccessToken: kernel.accessToken, + // }), + // ); + }, + }); + zlbInstance.destroy(); + }); +} + +export const PrivateInfoCard: React.FC = (props) => { + const [targetInfo, setTargetInfo] = useState<{ [key: string]: any }>(); + const [didload, setDidload] = useState(false); + const [qrcode, setQrcode] = useState(''); + + // 权限与是否显示 + const isSelf = props.space.id == props.target.id; + // 群权限 + const hasRelationAuth = props.session.target.hasRelationAuth(); + + const showPrivateCard = ['人员', '单位'].indexOf(props.target.typeName) > -1; + + const [SIManeger] = useState( + new SettingInfoManager(props.space.resource, hasRelationAuth), + ); + + useAsync(async () => { + try { + await SIManeger.init(); + if (!isSelf) { + setTargetInfo(await SIManeger.loadMemberInfo(props.target.metadata)); + } else { + setTargetInfo(await SIManeger.loadMemberInfo(orgCtrl.user.metadata)); + } + } catch (e) { + console.log('获取信息失败:', e); + } + setDidload(true); + }); + + const getContentWithMyComponent = () => { + return ( + { + if (SIManeger.privateInfo) { + SIManeger.privateInfo[field] = value; + await SIManeger.updated('PrivateInfo'); + } + }}> + ); + }; + + const getTargetMemberInfo = () => { + if (targetInfo) { + return ( + <> + { + targetInfo[field] = value; + await SIManeger.updated('memberInfo', targetInfo); + }} + /> + + ); + } else + return ( + <> +
该成员暂无信息
+ + ); + }; + + const getJoinContent = () => { + return ( + { + if (SIManeger.joinInfo) { + SIManeger.joinInfo[field] = value; + await SIManeger.updated('JoinInfo'); + } + }}> + ); + }; + + const identify = async () => { + switch (props.target.typeName) { + case '人员': + { + const res = await sendAuthData('/ZLB/GetRequestId', { + CertNo: SIManeger.privateInfo!.identificationNumber, + CertName: SIManeger.privateInfo!.name, + CertType: 1, + AccessToken: kernel.accessToken, + }); + zlbFaceAuth(res.data, setQrcode) + .then(() => {}) + .catch((errMsg) => message.error(errMsg)); + } + break; + case '单位': + // TODO 认证接口的调用 尚未开发相应单位认证流程 + break; + } + }; + const getAllContent = () => { + if (isSelf) { + return ( + <> + {showPrivateCard && hasRelationAuth && ( + <> + { + await identify(); + }}> + 认证 + + }> + {didload && <>{getContentWithMyComponent()}} + + {didload && <>{getJoinContent()}} + + {qrcode && ( + setQrcode('')}> +
+ +
+ 请使用浙里办App扫码进行人脸认证 +
+
+
+ )} + + )} + + {props.space.typeName == '单位' && ( + + {didload && <>{getTargetMemberInfo()}} + + )} + + ); + } else { + if (props.space.typeName == '单位') + return ( + <> + {getTargetMemberInfo()} + + ); + else + return ( + <> + {getTargetMemberInfo()} + + ); + } + }; + + return getAllContent(); +}; diff --git a/src/components/Directory/views/index.tsx b/src/components/Directory/views/index.tsx index a00edcf7f954a41333277eabb0fc1ce392f8520c..0f544be82744c561df39a6b3a54f3159fba66940 100644 --- a/src/components/Directory/views/index.tsx +++ b/src/components/Directory/views/index.tsx @@ -26,6 +26,7 @@ interface IProps { isMenu?: boolean; isNav?: boolean; isDynamic?: boolean; + showAuthTag?: boolean; customTags?: { tag: string; count: number }[]; badgeCount?: (tag: string) => number; tagChanged?: (tag: string) => void; @@ -59,13 +60,13 @@ const DirectoryView: React.FC = (props) => { const getContent = useCallback((filter: boolean = true) => { const filters = filterText.split('$$').filter((item) => item && item.length > 1); const filterExp = (file: IDEntity) => { - return filters.length < 1 || filters.some((item) => - file.code?.includes(item) || - file.name.includes(item) || - file.remark.includes(item) || - file.typeName.includes(item) || - file.filterTags.filter((i) => i.includes(item)).length > 0 || - file.groupTags.filter((i) => i.includes(item)).length > 0); + return filters.length < 1 || filters.some((item) => + file.code?.includes(item) || + file.name.includes(item) || + file.remark.includes(item) || + file.typeName.includes(item) || + file.filterTags.filter((i) => i.includes(item)).length > 0 || + file.groupTags.filter((i) => i.includes(item)).length > 0); }; if (props.extraTags) { if (filter && props.currentTag == '已选中') { @@ -133,6 +134,7 @@ const DirectoryView: React.FC = (props) => { /> ) : ( { const droppable = useRef(directory && directory.id != directory.target.id); @@ -42,6 +44,15 @@ const ListMode = ({ return isMenu ? css.vlist_item : `${css.vlist_item} ${css.border_bottom}`; }; + const renderAuthIconNode = (item: any) => { + const authed = item.metadata?.authenticated; + return ( + showAuthTag && ( + {authed ? '已认证' : '未认证'} + ) + ); + }; + const reorder = (list: IDEntity[], startIndex: number, endIndex: number) => { const result = Array.from(list); const removedItem = result.splice(startIndex, 1)[0]; diff --git a/src/executor/operate/joinTarget/index.tsx b/src/executor/operate/joinTarget/index.tsx index d158bab8166682c24d8d9756c85021db1687335b..202dd2c27c5e9b420742e2e7b8554d33f63ceac9 100644 --- a/src/executor/operate/joinTarget/index.tsx +++ b/src/executor/operate/joinTarget/index.tsx @@ -3,7 +3,10 @@ import { XTarget } from '@/ts/base/schema'; import { IBelong, TargetType } from '@/ts/core'; import SearchTarget from '@/components/Common/SearchTarget'; import { Modal } from 'antd'; -import { schema } from '@/ts/base'; +import { model, schema } from '@/ts/base'; +import { SettingInfoManager } from '@/components/DataPreview/session/settingInfo/privateInfo/PrivateInfo'; +import { DataResource } from '@/ts/core/thing/resource'; +import { FixedForm } from '@/components/DataPreview/session/settingInfo/privateInfo/fixedForm'; import { logger } from '@/ts/base/common'; type IProps = { @@ -17,8 +20,20 @@ type IProps = { */ const JoinTarget: React.FC = ({ cmd, current, finished }) => { const [selectMembers, setSelectMembers] = useState([]); // 选中的要拉的人 + const [showEditFormModal, setShowEditFormModal] = useState(false); + let useEditFormModal = true; let modalTitle = ''; let selectTargetType: TargetType = TargetType.Person; + + // 用于记录自己填写的信息 + const [SIManager, setSIManager] = useState(); + + const [apllyData, setApllayData] = useState({}); + + const [fixedFields, setFixedFields] = useState([]); + // 同步信息个人 刷新表格 + const [dataReloaded, setDataReloaded] = useState(true); + switch (cmd) { case 'joinFriend': modalTitle = '申请加好友'; @@ -30,6 +45,7 @@ const JoinTarget: React.FC = ({ cmd, current, finished }) => { case 'joinStorage': modalTitle = '申请加入存储资源群'; selectTargetType = TargetType.Storage; + useEditFormModal = false; break; case 'joinCompany': modalTitle = '申请加入单位'; @@ -46,30 +62,107 @@ const JoinTarget: React.FC = ({ cmd, current, finished }) => { default: return <>; } + const finishApply = () => { + logger.info('申请成功!请等待审批...'); + finished(); + }; + + const OpenApplyInfo = async () => { + const resource = new DataResource(selectMembers[0], [], [selectMembers[0].id]); + // 申请加入对象的信息管理 + const targetInfoManager = new SettingInfoManager(resource, false); + await targetInfoManager.init(); + const needData = targetInfoManager.joinInfo; + const slefResource = new DataResource(current.metadata, [], [current.id]); + const selfInfoManager = new SettingInfoManager(slefResource, true); + await selfInfoManager.init(); + if (needData) { + const fields: model.FieldModel[] = targetInfoManager.getMemberInfoFileds(); + if (fields.length > 0) { + setFixedFields(fields); + setSIManager(selfInfoManager); + setShowEditFormModal(true); + return; + } + } + if (await current.applyJoin(selectMembers)) finishApply(); + }; + + const getApplyInfoInput = () => { + return ( + <> + {useEditFormModal && showEditFormModal && ( + { + await SIManager?.saveApllyInfo( + current.metadata, + selectMembers[0], + apllyData, + ); + await current.applyJoin(selectMembers); + finishApply(); + }}> + {dataReloaded && ( + { + apllyData[field] = value; + }} + onSetButtonClick={() => { + // 同步自身信息 + setDataReloaded(false); + const data: { [key: string]: any } = {}; + fixedFields.forEach((field) => { + if (SIManager?.privateInfo) + data[field.id] = SIManager?.privateInfo[field.id]; + }); + setApllayData(data); + setTimeout(() => { + setDataReloaded(true); + }, 10); + }}> + )} + + )} + + ); + }; return ( - { - if (await current.applyJoin(selectMembers)) { - logger.info('申请成功!请等待审批...'); - finished(); - } - }} - onCancel={finished} - okButtonProps={{ disabled: selectMembers.length < 1 }} - width={670}> - { - setSelectMembers(persons); - }} - searchType={selectTargetType} - belongId={modalTitle === '申请加入部门' ? current.belongId : undefined} - code={modalTitle === '申请加入部门' ? current.metadata.code : undefined} - /> - + <> + { + if (!useEditFormModal) { + if (await current.applyJoin(selectMembers)) finishApply(); + } else { + await OpenApplyInfo(); + } + }}> + { + setSelectMembers(persons); + }} + searchType={selectTargetType} + belongId={modalTitle === '申请加入部门' ? current.belongId : undefined} + code={modalTitle === '申请加入部门' ? current.metadata.code : undefined} + /> + + {getApplyInfoInput()} + ); }; diff --git a/src/executor/tools/task/joinApply/index.tsx b/src/executor/tools/task/joinApply/index.tsx index 78cd94883232681986154f243ead45344377e627..d0112a4425a9de35a32dc1a9ae3ddfdfc5be029e 100644 --- a/src/executor/tools/task/joinApply/index.tsx +++ b/src/executor/tools/task/joinApply/index.tsx @@ -1,11 +1,15 @@ -import React from 'react'; -import { IWorkTask, TaskStatus } from '@/ts/core'; +import React, { useState } from 'react'; +import { ITarget, IWorkTask, TaskStatus } from '@/ts/core'; import EntityIcon from '@/components/Common/GlobalComps/entityIcon'; import { Divider, Space, Timeline, Card, Tabs } from 'antd'; import { formatZhDate } from '@/utils/tools'; import TaskApproval from '@/executor/tools/task/approval'; import { command } from '@/ts/base'; import cls from './index.module.less'; +import { useAsync } from 'react-use'; +import { SettingInfoManager } from '@/components/DataPreview/session/settingInfo/privateInfo/PrivateInfo'; +import { FixedForm } from '@/components/DataPreview/session/settingInfo/privateInfo/fixedForm'; +import { DataResource } from '@/ts/core/thing/resource'; export interface TaskDetailType { current: IWorkTask; @@ -13,6 +17,27 @@ export interface TaskDetailType { const TaskContent: React.FC = ({ current }) => { if (current.targets.length < 2) return <>; + const [AIManager] = useState( + new SettingInfoManager( + new DataResource(current.targets[0], [], [current.targets[0].id]), + false, + ), + ); + const [SIManager] = useState( + new SettingInfoManager( + new DataResource(current.targets[1], [], [current.targets[1].id]), + true, + ), + ); + const [apllyData, setApllayData] = useState<{ [key: string]: any }>(); + + useAsync(async () => { + await SIManager.init(); + const res = await AIManager.loadApplyInfo(current.targets[1]); + if (res) { + setApllayData(res); + } + }); /** 加载时间条 */ const loadTimeline = () => { @@ -36,6 +61,17 @@ const TaskContent: React.FC = ({ current }) => { 申请加入 + {apllyData && ( + <> +
申请人信息:
+ + + )} +
申请时间:{formatZhDate(current.taskdata.createTime)}
{current.taskdata.records && current.taskdata.records.length > 0 && ( <> @@ -63,8 +99,11 @@ const TaskContent: React.FC = ({ current }) => { { + finished={async () => { command.emitter('preview', 'work'); + if (apllyData) { + await SIManager.saveMemberInfo(apllyData); + } }} /> diff --git a/src/pages/Relation/index.tsx b/src/pages/Relation/index.tsx index 5b13fed2c2935ff6bd0619b4db533214de5fdb1c..124a280f824ba9688e4679de68a2dc92557f0191 100644 --- a/src/pages/Relation/index.tsx +++ b/src/pages/Relation/index.tsx @@ -126,6 +126,7 @@ const RelationBrowser: React.FC = () => { {renderHeader()} { // 报表树子节点 export type ReportTreeNodeView = WithChildren; -export interface ReportTaskTreeNodeView extends XReportTaskTreeNode { +export interface ReportTaskTreeNodeView extends XReportTreeNode { + // 子节点 children: ReportTaskTreeNodeView[]; + reception?: XReception | null; count: number; isLeaf: boolean; } @@ -2148,6 +2133,40 @@ export interface ReportTreeNode extends XReportTreeNode { index: number; } +interface AuthResponse { + responseCode?: string; + responseMessage?: string; + verification?: boolean; +} +export interface componyAuthReqType { + organizationName: string; + orgIdentificationType: string; + orgIdentificationNumber: string; + name: string; + identificationType: string; + identificationNumber: string; + remark?: string; +} + +export interface componyAuthResType { + code?: number; + message?: string; + result?: AuthResponse; +} +export interface personAuthReqType { + identificationType?: string; + identificationNumber: string; + name: string; + phoneNumber: string; + remark?: string; +} + +export interface personAuthResType { + code?: number; + message?: string; + result?: AuthResponse; +} + export type operationButtonInfo = { // 按钮名称 name: string; @@ -2167,3 +2186,37 @@ export type operationButtonInfo = { // 绑定场景 scene?: string; }; + +interface AuthResponse { + responseCode?: string; + responseMessage?: string; + verification?: boolean; +} +export interface componyAuthReqType { + organizationName: string; + orgIdentificationType: string; + orgIdentificationNumber: string; + name: string; + identificationType: string; + identificationNumber: string; + remark?: string; +} + +export interface componyAuthResType { + code?: number; + message?: string; + result?: AuthResponse; +} +export interface personAuthReqType { + identificationType?: string; + identificationNumber: string; + name: string; + phoneNumber: string; + remark?: string; +} + +export interface personAuthResType { + code?: number; + message?: string; + result?: AuthResponse; +} \ No newline at end of file diff --git a/src/ts/core/target/base/team.ts b/src/ts/core/target/base/team.ts index 01c3c47f27f63b3296e3814dcadc7111a30e2545..73559a5d28a4226cb2138e2a76f8c8b32b3cb82e 100644 --- a/src/ts/core/target/base/team.ts +++ b/src/ts/core/target/base/team.ts @@ -356,4 +356,4 @@ export abstract class Team extends Entity implements ITeam { }); return res.data || { offset: offset, limit: 2000, result: [] }; } -} +} \ No newline at end of file diff --git a/typings/typing.d.ts b/typings/typing.d.ts index 68a1561066275a861ed3c79c7f91d35131b960fd..4476e320f6aa49074852927c3827599420f75455 100644 --- a/typings/typing.d.ts +++ b/typings/typing.d.ts @@ -4,3 +4,7 @@ declare module 'less-vars-to-js'; declare module 'vite-plugin-style-import'; declare module 'pako'; declare module 'react-office-viewer'; + +interface Window { + _GovAuthentication: any; // 浙里办人脸认证 +} diff --git a/vite.config.ts b/vite.config.ts index 5ccb7dbb14e4507ba84ddd218d98dc49304a9493..7463693c3f6c6ab14abc32ad0e24b139f89c36f6 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -38,7 +38,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { // 是否主动唤醒浏览器 open: false, // 占用端口 开发环境启动的端口 - port: 8080, + port: 8081, // 是否使用https请求 // https: true, // 扩展访问端口 @@ -53,6 +53,11 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { changeOrigin: true, // 是否允许跨域 ws: true, }, + '/ZLB': { + target: 'https://asset.orginone.cn', // 后台接口 + changeOrigin: true, // 是否允许跨域 + ws: true, + }, }, }, build: { @@ -98,7 +103,7 @@ export default ({ command, mode }: ConfigEnv): UserConfig => { 'html2canvas', '@wangeditor/editor', '@wangeditor/editor-for-react', - ], + ], 'xlsx-vendor': ['xlsx'], 'play-vendor': ['jol-player'], 'dev-vendor': ['devextreme-react'],