资讯详情

资讯详情

PDMS-AVEVA-E3D二次开发教程(10):元件库与等级自动化——Catalogue / Specification 能被代码碰到什么

PDMS-AVEVA-E3D二次开发教程10元件库与等级自动化——Catalogue / Specification 能被代码碰到什么版本与事实声明组件数据来自目录catalogue、规格Specification引用目录数据这一核心机制取自官方 Getting Started 的 MODEL 数据库元素类型章节结构类由 Section Specification 引用目录数据亦为官方原文。目录数据的元素类型清单来自官方 Data Model Reference ManualDMRM目录具体字段与内部表结构以官方 Catalogue 参考手册为准本系列 U4 项——本篇不编造目录字段名。本篇代码采用属性名参数化 ATTLIS 预校验的写法延续第 06 篇因为具体属性名必须由你在自己的版本上用ATTLIS核对后填入。所有代码块只使用官方文档中出现过的语法。示例管径、等级名、计数均为示例性数据不代表任何标准规定。一句话结论E3D 里管道元件与结构型钢的尺寸数据只存在于组件目录catalogue中一份模型上的每个元件只是对目录数据的一次引用官方原文无论用多少个 100mm 弯头它的数据始终来自组件目录且保持不变因此二次开发的正确姿势是——从模型侧反查用了哪些规格和口径组合并做对账而不是试图去改目录里的数据。〇、认知问题Q1E3D 里设计数据和目录数据是两套东西吗它们的关系是什么Q2官方那句无论用多少个 100mm 弯头数据始终来自组件目录且保持不变对二次开发意味着什么Q3规格Specification在目录数据和模型之间扮演什么角色结构专业为什么是另一套规格Q4等级规格自动化到底能做什么、不能做什么边界在哪Q5怎么做规格与等级的对账才能既发现问题又不越权改数据一、机制解析1.1 两类数据设计数据与目录数据官方 Getting Started 在讲 MODEL 数据库层级时顺带把最重要的一条数据设计原则交代清楚了“Piping components are selected using Piping Specifications that reference standard catalogue data. For example, each time the user wants to use a 100mm bore elbow, AVEVA E3D™ always accesses the data for it from the component catalogue. The data for this remains constant no matter how many 100mm bore elbows are used in the design.”结构专业同理官方原文是结构型钢由 general sectionGENSEC元素表示工字钢截面尺寸通过Section Specification引用符合各国标准的目录数据来选取。于是有两套数据设计数据MODEL 等目录数据Catalogue存什么这个项目里有哪些元件、装在哪、什么状态“某标准某口径的弯头长什么样”——尺寸、重量、材料类别数量特征随设计增长几千几万个元件相对稳定规格化、可复用谁改设计人员目录/标准管理人员程序怎么读到遍历模型、读属性通过规格间接得到或按官方接口访问目录官方 DRAW 帮助文档的目录里同时出现“Accessing Data from the DESIGN or Catalogue Databases”与“Accessing Data in Catalogue Datasets”两节——说明从出图模块也能访问目录数据这类数据是产品的一等公民不是内部实现细节。1.2 只存一份意味着什么本篇最重要的推论官方那句数据保持不变无论用了多少个是数据规范化的经典表述。对二次开发它推导出四条必须记住的推论推论一模型上的元件是引用改模型不改目录。你在模型里改一个弯头的尺寸在数据层面通常不是改这个弯头而是换一个引用换成另一个目录项。这意味着脚本无法通过改模型来实现改标准——那需要改目录数据属于目录管理职责。推论二改目录数据会影响所有引用它的项目。这是目录数据危险性的来源。因此铁律 4破坏性操作先离线验证在目录数据上要求更严——目录通常是多项目共享的。推论三模型里出现的规格/口径组合是有限且可枚举的。这正是等级自动化的立足点你不需要遍历几万个元件才能知道这个项目用了哪些规格与口径——你只需要统计出组合集合本篇代码 10-1。这是从大数据的处理变成小集合的处理的降维。推论四目录数据的完整性无法从模型侧证伪。模型侧只能告诉你用到了什么不能告诉你目录里该有而没有的是什么。所以等级完备性这件事模型侧只能做半张检查1.5 节展开。1.3 三层路径目录 → 规格 → 模型把 1.1 节的关系画成一条链┌──────────────────────────────────────────────────────────┐ │ ① 组件目录Catalogue │ │ 标准化的元件数据集截面尺寸、壁厚表、重量、材料类别 │ │ 特点与项目无关、可跨项目共享 │ └───────────────────────────┬──────────────────────────────┘ │ 被引用不是被复制 ┌───────────────────────────┴──────────────────────────────┐ │ ② 规格Specification │ │ 管道Piping Specification本项目/本装置用什么 │ │ 结构Section Specification用什么截面标准 │ │ 特点项目级、含选择规则口径范围、压力等级、材料 │ └───────────────────────────┬──────────────────────────────┘ │ 建模时按规格选取 ┌───────────────────────────┴──────────────────────────────┐ │ ③ 模型元件MODEL 数据库中的 BRAN 及其下的 FLAN/VALV/… │ │ 特点项目级、成千上万、只记录引用 少量项目属性 │ └──────────────────────────────────────────────────────────┘为什么必须记住这三层因为它决定了一条数据问题的归属症状通常归属层谁能改“所有项目里的这个弯头重量都不对”① 目录标准/目录管理员“这个项目的这个等级选错口径范围”② 规格等级表维护人“这根管道上这个阀装反了”③ 模型设计人员最佳实践写任何元件相关的脚本之前先回答我改的是哪一层与铁律 4 的离线验证直接挂钩。归属定错脚本就会去动不该动的东西。1.4 目录数据的元素类型族先认识名字再谈访问官方 Data Model Reference ManualDMRM的目录里出现了一批与数据/目录/数据集相关的元素类型至少包括元素类型缩写名称DATAData ElementINFITTINGInformation Fitting ElementREFDATRef Data Group ElementDATDEFDerived Data Definition ElementDERDATDerived Data ElementDDATADesign Data ElementDDSEDesign Dataset ElementPLDATUMPline Datum ElementSYSCDASystem Catalogue Data ElementSYSMDASystem Dataset ElementAPPL…Application Data Element怎么用这张表它的价值是让你在手册里找得到东西。当你要查某个数据项是怎么定义的先看它属于哪一类但它的字段与内部结构本系列不写——U4 项明确“Catalogue/Specification 的内部表结构与字段名只写官方概念层描述 以 Catalogue 参考手册为准”。经验法则对目录数据只读不写。目录是全项目的共享资产脚本对它做的任何顺手清理/顺手补全都可能影响别的项目且往往没有回滚余地。1.5 等级自动化的能力边界把等级自动化能做的事列清楚就不容易越界能做模型侧安全能做目录侧需权限与流程不能做职责边界外统计本项目用了哪些规格/口径/材料组合按官方接口查询目录数据做完整性核对判断这个等级设计是否合理检查模型元件的口径是否落在其规格允许范围内生成等级数据使用报表自动修正目录里的尺寸或重量检查同一根管道上是否混用了不同规格比对两个项目/两个版本的等级差异决定标准应该怎么改输出待确认清单例如模型里出现未在预期清单中的组合输出目录数据的元数据报表替代标准管理人员的签字一句话边界模型侧能做的都建立在模型是引用的集合这个事实上目录侧的每一项动作都需要先取得目录管理权限与流程许可。这也是为什么本篇的代码全部落在左列。1.6 反查与对账正确的问题问法等级对账的常见需求是模型用的东西等级表里到底有没有。这句话里其实混了两个问题问题数据在哪谁答得了模型用到了哪些规格/口径组合模型引用侧你的脚本本篇代码 10-1这些组合在目录里是否都有定义目录有目录访问权限的人/官方报表工具最佳实践把前者做成脚本的稳定产出把后者做成清单交付 人工/有权限工具核对。这样你的脚本不需要碰目录却已经完成了对账工作量的 80%——因为待核对清单已经被压缩成有限集合1.2 节推论三。而且这个产出天然带审计价值一份本项目规格使用情况表本身就是等级表维护人最想要的东西。1.7 权限与数据访问控制官方数据手册System Administration 相关章节提到数据访问控制DACs其说明指出定义 Scope 的语法与 PML 语法类似关键字如 ALL可用于 DAC 上下文示例形态ALL WITH NAME OF SITE EQ ——该信息来自第三方转述级材料仅作背景具体语法以官方 System Administration 文档为准。工程含义很实际你的脚本能读到什么、能写到什么取决于当前账户的权限范围。因此批处理第 09 篇在探测环境那一段应当把当前用户与会话也记录下来当脚本在 A 机器能跑、B 机器报无权限时第一反应是权限差异而不是代码差异。同时官方 DBRM 有Status Control与Claim机制模型元素常有状态如发布状态与占用Claim概念。最佳实践等级检查类脚本应当默认排除已发布Released状态的元素或至少把它们单独计数——因为发布后的元素即便有问题也应当走变更流程而不是被脚本列入待改清单。二、完整代码与逐行剖析代码 10-1规格使用情况统计PML可运行属性名参数化-- 文件myco_specusage.pmlf -- 用途统计指定范围内规格 × 口径的使用情况输出可对账的清单 -- 设计要点 -- * 规格属性名与口径属性名【参数化】——具体属性名先用第 06 篇的 AttrProbe 在本机核对 -- * 只读铁律 4输出压缩成组合集合规模远小于元件总数1.2 节推论三 -- * 组合集合用数组 线性查找维护不臆造内置去重方法 define function !!MYCO_SpecUsage( !scopeRef is DBREF, !specAttr is STRING, !boreAttr is STRING ) is STRING !start !!ce if ( BADREF( !scopeRef ) ) then return |Failure: scope DBREF invalid| endif if ( !specAttr eq or !boreAttr eq ) then return |Failure: attribute names must be provided explicitly| endif !!ce !scopeRef !scopeName name !scopeType type -- 用 ATTLIS 预校验属性名区分属性名写错与属性未设置第 06 篇的教训 !attlis !!ce.ATTRIBUTE( ATTLIS ).String() !keys ARRAY() -- 组合键规格名 \t 口径文本 !counts ARRAY() -- 每个组合的计数 !nKeys 0 !nItems 0 !nNoSpec 0 handle any !!ce !start return |Failure: unexpected error during spec usage scan| endhandle var !items coll all for $!it do !it values !items !!ce !it.dbref() !nItems !nItems 1 !sp !!ce.ATTRIBUTE( !specAttr ) if ( not ( Defined( !sp ) ) ) then !nNoSpec !nNoSpec 1 skip endif !bo !!ce.ATTRIBUTE( !boreAttr ) if ( not ( Defined( !bo ) ) ) then !boText |no-bore| else !boText !bo.String() endif !key !sp.String() |\t| !boText -- 线性查找组合集合规模有限通常几十到几百线性查找足够 !idx 0 !k 1 do !k from 1 to !nKeys if ( !keys[!k] eq !key ) then !idx !k break endif enddo if ( !idx gt 0 ) then !counts[!idx] !counts[!idx] 1 else !nKeys !nKeys 1 !keys[!nKeys] !key !counts[!nKeys] 1 endif enddo !!ce !start -- 组装对账清单限制输出规模避免日志爆炸 !out || !shown 0 !k 1 do !k from 1 to !nKeys if ( !shown ge 100 ) then break endif !out !out !keys[!k] |\t| !counts[!k].String() \n !shown !shown 1 enddo return |Success| |\t| |scope| !scopeName |(| !scopeType |)| |\t| |items| !nItems.String() |\t| |combos| !nKeys.String() |\t| |no_spec| !nNoSpec.String() |\n| |attrlist_head| !attlis.Left( 60 ) |\n| !out endfunction逐行剖析两个属性名都是参数这是刻意的。管道规格与口径具体叫什么属性名必须在你的产品版本上用ATTLIS核对第 06 篇。把名字做成参数好处是——核对结果只需改调用处不用改函数而且同一个函数可以复用于别的属性组合例如材料 × 口径。第 6 行开始的ATTLIS预校验!attlis被截取前 60 个字符丢进返回值。为什么不完整返回因为属性列表可能很长日志要控制规模但保留一段能让你确认ATTLIS 确实取到了东西若为空则说明属性查询接口在当前上下文不适用。这是轻量自证的写法不增加多少输出却能在排障时省一轮。var !items coll all for $!it不带条件的全量集合。注意这里与第 09 篇的差别——这里故意取全量因为统计口径需要所有项而不是待处理项。该带条件的地方不带是浪费该全量的地方硬加条件是错误判据是你要回答的问题需不需要完整分布。手写线性查找做去重为什么不用内置去重方法因为官方数组章节里虽有 “Array Methods”、“Sorting Arrays Using Array Methods” 等节具体方法名本系列不臆造U2。手写线性查找在组合数有限时性能完全够用且行为完全可预测。这是一个重要的工程态度当不确定内置方法的确切行为时用一个慢但确定的实现比用一个快但可能用错的方法要好——尤其是只读统计脚本慢几秒没有任何代价。break命中即停官方 “Stopping a DO loop: break and breakif” 一节。这里用break而不是breakif是风格选择——break放在if里更易读breakif更适合条件即中止的紧凑写法第 04 篇讲过两者分工。no_spec单独计数这是本函数最有业务价值的一列。模型里有元素没有规格是等级管理的头号问题——它意味着这个元件脱离了等级体系可能是建模时手工放的或规格数据缺失。单独计数而不是简单跳过正是为了让这个问题浮出来。输出规模限制在 100 行if ( !shown ge 100 ) then break。理由write()到命令窗口的内容过多会拖慢运行而且超过屏幕的信息没人看。真实项目应当把完整清单落盘落盘机制以官方文件对象或 OUTPUT/Datal 机制为准第 12 篇展开命令窗口只报总数 摘要。返回契约固定列Success / scope / items / combos / no_spec另起一行放明细。代码 10-2等级命名规范检查PML可运行规格名混乱是等级管理里的常见病大小写不一、缺前缀、带空格。这一段专门治它——纯文本逻辑不依赖任何未确证的接口。-- 文件myco_specname.pmlf -- 用途检查规格名是否符合团队命名规范 -- 规范示例由团队确定后不得随意更改 -- 1) 必须以大写字母开头 -- 2) 必须以 P 或 S 开头P管道 PipingS结构 Section -- 3) 不得包含空格 -- 4) 长度不得超过 32 个字符 define function !!MYCO_SpecNameCheck( !specName is STRING ) is STRING if ( !specName eq ) then return |Failure: spec name is empty| endif !reasons || !len !specName.Length() if ( !specName.Left( 1 ) ne |P| and !specName.Left( 1 ) ne |S| ) then !reasons !reasons |prefix; | endif if ( !specName.Upper() ne !specName ) then !reasons !reasons |not-uppercase; | endif if ( !len gt 32 ) then !reasons !reasons |too-long; | endif -- 空格检查用手写遍历避免臆造查找子串的方法名 !hasSpace false !i 1 do !i from 1 to !len if ( !specName.Substring( !i, 1 ) eq | | ) then !hasSpace true break endif enddo if ( !hasSpace ) then -- 无空格不追加原因此处直接继续便于阅读 !len !len else !reasons !reasons |contains-space; | endif if ( !reasons eq ) then return |Success: | !specName | conforms| endif return |Failure: | !specName | violates: | !reasons endfunction逐行剖析这是零依赖函数不读数据库、不碰会话状态、不需要!!ce保护——因为它什么都不改。这是分层的好处第 07 篇三层架构纯逻辑函数可以做到完全无副作用从而可以放心地被到处调用、被批处理调用、被单测覆盖。!len !len这一行看起来是废话实际是刻意留的它标出这里是一个分支但不产生原因的位置。真实项目里这种空分支应当被删掉并重构成扁平逻辑示例保留它是为了让你看到评审时的坏味道长什么样能用if / else表达的东西不要写成if / 空操作。空格检查用手写遍历因为查找子串的方法名本系列不臆造。Substring 类方法的精确签名以官方 Text FunctionsDBRM9.10.26与 PML 字符串方法章节为准。!specName.Upper()大小写转换。注意 PML 本身大小写无关但这是语言层面的无关——字符串内容的大小写仍然是数据。这两件事经常被混为一谈于是有人觉得规格名大小写不重要。在数据层面它非常重要不同工具、不同脚本对SPEC-A与spec-a的判定不同。返回|Failure: 名字 violates: 原因列表失败信息里带具体原因而不是只说不符合规范。经验法则失败信息里没有原因的脚本等于要求使用者自己去猜第 08 篇代码 8-1 也守这条。代码 10-3对账清单的交付格式CSV 样例section,item,value,source,action_required,owner scope,SITE /AREA-A,-,MYCO_SpecUsage,-,piping-lead usage,combos,47,-,-,piping-lead usage,no_spec,6,元素无规格属性,Y,piping-lead spec_name_check,P-1A2A3-150,conforms,-,N,piping-lead spec_name_check,spec-1a2a3-150,violates: prefix; not-uppercase;,MYCO_SpecNameCheck,Y,piping-lead cross_check,P-1A2A3-150 x DN150,combos23,-,N,catalogue-admin cross_check,P-1A2A3-300,count1, 异常低频,MYCO_SpecUsage,Y,piping-lead逐行剖析section列区分清单的三类来源usage使用情况、spec_name_check命名检查、cross_check需目录侧核对项。这个分类是本表的灵魂——它一眼就把我能判定的和必须别人判定的分开了1.6 节的分工。owner列写谁该处理piping-lead配管负责人、catalogue-admin目录管理员。交接给对方时可以直接按 owner 过滤不需要口头解释。action_required用Y/N而不是需要/不需要留给机器判断。经验法则任何会被人按列筛选的字段取值集合要短且稳定。source列写这一行是哪个函数产生的可追溯性。三个月后有人问这个 6 是怎么来的答案在表里。注意P-1A2A3-150 x DN150这类交叉检查行它的owner是catalogue-admin——这就是把不可判定项交出去的形态而不是在脚本里假装能判。三、常见报错与排查报错 3-1脚本读不到规格/口径属性全部落入no_spec。现象统计结果异常。根因按概率排序①属性名写错第 06 篇产品对拼错的属性名不报错只返回 UNDEFINED② 属性在目标元素类型上不存在管道属性查到了结构元素上③ 确实有大量元素未设置该属性这才是真实业务问题。解法先用第 06 篇AttrProbe打印ATTLIS核对属性名本函数已把attrlist_head放进返回值就是为这件事核对后再看no_spec是业务问题还是脚本问题。报错 3-2改了脚本的一处属性名结果整个统计都变了。现象无法解释的变化。根因属性名参数化后调用处传错了参数例如把boreAttr与specAttr顺序写反。解法在函数入口用!specAttr eq or !boreAttr eq 做非空校验本函数已有并在返回文本里把两个属性名原样回显。这是参数化必须付的代价——参数越多传错的可能性越大所以回显是必需的。报错 3-3脚本顺手改了目录数据影响到了别的项目。现象跨项目影响。根因越过了 1.3 节的三层归属——把本该属于目录层①的修改通过脚本直接执行了。而且常常是为了修一个项目的显示问题。解法把脚本的写入能力彻底移除本篇范式只读目录侧的任何变更走目录管理流程同时用官方数据访问控制DAC机制限制脚本账户的写权限——用权限做物理隔离比靠纪律更可靠。报错 3-4统计出来的组合数远超预期几百上千。现象组合爆炸。根因把不该作为维度的属性放进了组合键。例如把每个元件的唯一编号混进键里就等于没做聚合。解法组合键的每一项都必须是有限取值的属性规格名、口径、材料、等级不要放自由文本字段描述、备注、编号。可用一个简单判据如果某个键的取值数量与元素数量同量级它就不是维度。报错 3-5脚本在 A 账号能跑、B 账号报无权限。现象权限差异。根因数据访问控制DAC范围不同或元素状态如已发布限制了读写。解法把当前用户与会话纳入批处理的环境探测输出第 09 篇等级检查类脚本默认排除已发布状态元素或单独计数——因为发布后的元素应走变更流程而不是被脚本列入待改清单。四、动手练习练习 1属性名核对用第 06 篇的AttrProbe对一根管段和一个结构框架各跑一次把与本篇相关的属性名规格类、口径类、截面类记进笔记。判定标准至少记录 3 个属性名并注明它们各出现在哪类元素上对 3 个名字都做了用代码 10-1 读一次并确认非 UNDEFINED的验证。练习 2组合统计把代码 10-1 在目标范围上跑通产出组合清单。判定标准combos数量显著小于items数量若两者同量级说明你把自由文本当成了维度按报错 3-4 排查no_spec单独计数且能列出至少一个具体元素。练习 3命名规范用代码 10-2 检查你项目里的全部规格名。判定标准不符合规范的名字与符合的名字都能被正确分类每条失败结果里的violates:原因列表非空且与规范条目一一对应对仅大小写不同的两个规格名如P-A与p-a能正确判为后者不合规。练习 4对账交付把练习 2、3 的结果整理成代码 10-3 的 CSV 格式。判定标准section列至少包含usage与cross_check两类owner列对cross_check行填的是目录侧角色不是你自己action_required列取值只有Y/N用 Excel 打开时中文不乱码即编码为带 BOM 的 UTF-8。思考题无标准答案官方为什么把组件数据设计成规格引用目录、目录只存一份而不是每个元件自带全部尺寸数据验证要点① 从数据量角度看两种设计的差异想想几千个同规格弯头② 从改标准的影响面角度看两种设计的差异改一处 vs 改一万处③ 这种设计给你的二次开发带来了什么约束提示你无法通过改模型来改标准以及你该怎么在脚本里体现这个约束。五、小结与下一篇预告E3D 有设计数据与目录数据两类管道元件与结构型钢的尺寸数据只存在于组件目录中一份模型上的元件是引用而非副本官方原文无论用多少个 100mm 弯头数据始终来自组件目录且保持不变。数据路径是目录 → 规格 → 模型三层出问题时先判定归属层再动手。等级自动化的可行范围在模型侧统计规格与口径的组合把大数据降维成有限集合、在模型内做一致性检查、输出待核对清单目录侧的每一项动作都需要权限与流程。对账的正确问法是把它拆成模型用了什么你的脚本答与目录里有没有有权限的人/工具答。最后两条纪律对目录数据只读不写组合键的每一项都必须是有限取值的属性。下一篇《规则检查与碰撞把设计质量做成自动化》从数据统计转向设计质量判定。我们讲清官方的碰撞检测机制——障碍等级 OBST 取 2/1/0 的含义、Physical clash / Touch / Clearance 三种判定的阈值逻辑官方示例重叠 5mm 报 clash、间隙 2mm 报 touch、间隙 8mm 报 clearance、clearance 设为 0 即关闭该项检查——以及 3.1.4.0 引入的 Rule Manager 与规则函数接口中的四个固定名元素CEREF / RESULT / RULEMETHOD / OBSREF。本篇认知问题回显FAQQ1E3D 里设计数据与目录数据是什么关系A两套数据。设计数据MODEL记录项目里有哪些元件、装在哪、什么状态随设计增长目录数据Catalogue记录某标准某口径的元件长什么样——尺寸、重量、材料类别与项目无关、跨项目共享。官方原文说明管道元件由 Piping Specification 引用标准目录数据选取且无论用多少个 100mm 弯头其数据始终来自组件目录且保持不变结构型钢由 Section Specification 引用目录数据同理。Q2数据只存一份对二次开发意味着什么A四条推论模型上的元件是对目录项的引用改模型不等于改标准改目录数据会影响所有引用它的项目因此目录侧操作风险更高模型里出现的规格与口径组合是有限可枚举的这使等级自动化可以把大数据降维成小集合目录数据的完整性无法从模型侧证伪模型侧只能告诉你有用了什么不能告诉你该有而没有什么。Q3规格在目录和模型之间扮演什么角色A规格是中间层分两类管道用 Piping Specification项目级含选择规则如口径范围、压力等级、材料结构用 Section Specification引用符合各国标准的截面目录数据。三层归属决定问题该由谁改所有项目都错往往是目录问题、本项目选错是规格问题、单根管道错是模型问题。写脚本前必须先回答我改的是哪一层。Q4等级自动化能做什么、不能做什么A模型侧可做统计规格与口径组合、检查元件口径是否落在其规格允许范围内、检查同一管道是否混用规格、输出待确认清单。目录侧需权限与流程查询目录数据做完整性核对、生成等级使用报表、比对版本差异。不能做判断等级设计是否合理、自动修正目录尺寸或重量、决定标准怎么改。总原则是对目录数据只读不写。Q5怎么做规格与等级对账才既不越权又有效A把需求拆成两个问题模型用到了哪些规格与口径组合模型侧可答由脚本产出这些组合在目录里是否都有定义目录侧由有权限的人或官方报表工具核对。脚本只需产出稳定的待核对清单因为组合已被压缩成有限集合清单应分类标注来源与责任人如 catalogue-admin并用 action_required 的 Y/N 标记是否需处理从而把不可判定项明确交出去。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →