宅急送 项目第七天 取派业务模块

本文详细介绍了宅急送项目中取派业务模块的开发过程,包括业务分析、数据库设计和功能实现。重点讲解了业务受理功能,如业务通知单录入、自动分单和工作单管理,以及利用Hibernate Search进行全文索引提升模糊查询性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 取派模块业务分析

已经完成基础设置模块 : 收派标准管理、 取派员管理、 区域管理、分区管理、定区管理
基础设置模块的数据,实际上为后期业务模块提供支持

这里写图片描述

这里写图片描述

重点开发任务,业务受理模块

业务受理功能
受理环节,是宅急送业务的开始,作为服务前端,客户通过电话、网络等多种方式进行委托,业务受理员通过与客户交流,获取客户的服务需求和具体委托信息,将服务指令输入我司服务系统。

业务受理后,业务人员需要将客户信息,录入为 “业务通知单”(代表客户一份快递需要) ,系统进行自动分单,产生“工单” , 系统自动识别客户对应定区的负责人员, 由指定取派员到客户家取货

工作单功能 “工作单”
工作单基础信息的录入是信息的入口,并且也是本系统中人机交互最多的环节;基础信息录入的及时、准确、完整会直接影响后续的实物的运转配载、小件员交款、财务收入确认、成本垫付分摊,所以对数据的准确性、及时性、完整性要求比较高。

业务通知单: 客户快递申请
工单: 取派员取件任务
工作单 : 面向物流的数据,真实货物信息

这里写图片描述

查台转单
当自动下单成功,但是由于基础信息错误,而导致分配的取货人员错误,通过查台转单来解决此类失误。 (分错取派员,更换取派员 )

人工调度
在自动下单的时候,由于取件地址无法匹配取件人员,就转入人工调度。 (业务人员,手动为客户,指定取派员 )

2. 取派业务模块— 数据库设计

根据 “业务受理数据表.xls” 设计PowerDesigner PDM 物理数据模型
参考 “业务受理.pdm”

根据PDM生成SQL 脚本,将脚本导入 数据库

这里写图片描述

qp_noticebill 业务通知单
qp_workbill 工单
qp_workordermanage 工作单

根据数据表,使用MyEclipse 反转生成PO类和hbm映射

这里写图片描述

修改生成的类名

这里写图片描述

3. 业务受理功能

客户提交快递申请 ,业务人员将客户业务信息,录入到系统

这里写图片描述

3.1. 业务通知单页面编写

业务通知单录入页面 : /WEB-INF/pages/qupai/noticebill_add.jsp
页面存在大form 表单 ,使用EasyUI form 控件

validatebox (验证框) :非空、长度 
<input type="text" class="easyui-validatebox" required="true" />

numberbox (数字框) :对validatebox扩展,只允许输入数字 
<input type="text" class="easyui-numberbox" required="true" />

combobox (下拉框) : 可以对 <input> 元素,生成下拉列表 

datebox (日历控件) : 选择日期 
<input type="text" class="easyui-datebox" data-options="required:true,editable:false"/>

检查业务受理form表单,元素name属性和 NoticeBill 实体类是否一致
为form添加 点击提交事件

// 点击新单按钮,将业务通知单 保存
$('#save').click(function(){
    if($("#noticebillForm").form('validate')){
        $('#noticebillForm').submit();
    }else{
        $.messager.alert('警告','表单存在非法数据项!','warning');
    }
});

3.2. 编写业务受理服务器代码

3.2.1. 完成基本代码结构
public class NoticeBillAction extends BaseAction implements ModelDriven<NoticeBill> {
}
public interface NoticeBillService {
}
public class NoticeBillServiceImpl extends BaseService implements NoticeBillService {
}

将DAO 注入Service
将Service 注入 Action

在业务受理时,产生业务通知单的数据,在保存到数据库的时候,需要进行自动分单的操作

1、 用客户取货地址通过远程调用,访问CRM,比较CRM系统中客户地址 ,查找客户对应定区,再通过定区获得 取派员信息 
2、 如果不能在CRM中找到客户对应定区,使用当前发件人地址去匹配分区地址 ,就可以通过分区去管理定区,匹配到取派员 

如果自动分单成功,需要在数据表 qp_workbill 生成一条工单记录 !
如果自动分单失败,进入人工调度环节

3.2.2. 自动分单 — 去CRM匹配客户的地址

这里写图片描述

修改CRM业务接口 CustomerService

// 根据 客户地址 查询 定区编码
public String findDecidedZoneIdByCustomerAddress(String address);

这里写图片描述

将修改后的CustomerService 接口,复制BOS系统 !!!
编写 CustomerServiceTest测试用例 ,完成接口调试

3.2.3. 自动分单 —- 匹配分区的关键字
<query name="Subarea.findByAddresskey">
        <![CDATA[from Subarea where addresskey = ?]]>
</query>
public void saveNoticeBill(NoticeBill noticeBill) {
    // 将业务通知单数据保存到数据库
    noticeBillDAO.save(noticeBill);

    // 自动分单
    // 1 、使用当前取件地址,去查询CRM系统 定区编码
    String decidedZoneId = customerService.findDecidedZoneIdByCustomerAddress(noticeBill.getPickaddress());
    if (decidedZoneId == null) {
        // 未查到
        // 2、匹配分区 关键字
        String[] addressArray = noticeBill.getPickaddress().split(" "); // 北京市海淀区 xxx路 1号楼
        if (addressArray.length >= 2) {
            String addresskey = addressArray[1]; // 取第二个元素 作为关键字
            List<Subarea> list = subareaDAO.findByNamedQuery("Subarea.findByAddresskey",addresskey);

            // 只匹配到唯一的一个分区,而且这个分区已经关联到定区
            if (list.size() == 1 && list.get(0).getDecidedZone() != null) {
                // 自动分单成功
                // 查到 (自动分单成功)
                DecidedZone decidedZone = list.get(0).getDecidedZone();

                // 通知单
                noticeBill.setStaff(decidedZone.getStaff());
                noticeBill.setOrdertype("自动");

                // 工单信息
                WorkBill workBill = new WorkBill();
                workBill.setNoticeBill(noticeBill);
                workBill.setStaff(decidedZone.getStaff());
                workBill.setType("新");
                workBill.setPickstate("新单");
                workBill.setBuildtime(new Timestamp(System.currentTimeMillis()));
                workBill.setAttachbilltimes(0);
                workBill.setRemark(noticeBill.getRemark());
                workBillDAO.save(workBill);
            } else {
                // 人工调度
                noticeBill.setOrdertype("人工");
            }
        } else {
            // 人工调度
            noticeBill.setOrdertype("人工");

        }
    } else {
        // 查到 (自动分单成功)
        DecidedZone decidedZone = decidedZoneDAO.findById(decidedZoneId);

        // 通知单
        noticeBill.setStaff(decidedZone.getStaff());
        noticeBill.setOrdertype("自动");

        // 工单信息
        WorkBill workBill = new WorkBill();
        workBill.setNoticeBill(noticeBill);
        workBill.setStaff(decidedZone.getStaff());
        workBill.setType("新");
        workBill.setPickstate("新单");
        workBill.setBuildtime(new Timestamp(System.currentTimeMillis()));
        workBill.setAttachbilltimes(0);
        workBill.setRemark(noticeBill.getRemark());
        workBillDAO.save(workBill);
    }
}

3.2.4. 配置添加通知单Action

<!-- 业务通知单受理 -->
<action name="noticebill_*" class="noticeBillAction" method="{1}">
    <result name="saveSUCCESS">
        /WEB-INF/pages/qupai/noticebill_add.jsp
    </result>
</action

>

4. 业务受理 未实现的功能

工单操作

这里写图片描述

查询出系统的所有工单 qp_workbill

追单 : 客户已经提交业务申请,取派员很多都没有去客户家中取件,再次进行追单操作, 系统只需要在 workbill表中 attachbilltimes 追单次数+1 (实际业务中,需要通过短信平台,催促取派员 )

销单 : 在取派员去取件的过程中,客户选择进行销单,不进行快递业务 ,在workbill 表type 设置 “销”, pickstack 设置为 “已取消”

查台转单
业务通知单 自动分单 成功 ,但是分错人了, 需要为通知单 重新手动更换 取派员 !

人工调度
业务通知单 自动分单失败,进入人工调度,需要手动为通知单指定取派员,生成工单!

结果: 已经将客户的货物,取回了物流公司!!!

5. 工作单管理

工作单基础信息的录入是信息的入口,并且也是本系统中人机交互最多的环节;基础信息录入的及时、准确、完整会直接影响后续的实物的运转配载、小件员交款、财务收入确认、成本垫付分摊,所以对数据的准确性、及时性、完整性要求比较高。

5.1. 工作单快速录入

提供操作人员在货量大单子多时简洁快速录入工作单的途径,快速录入中的信息主要是为了满足配载而设置的功能界面。
当业务高峰期,业务人员,只需要录入工作单基本信息(为了满足物流),就可以开始物流配送, 在业务闲暇时,再手动完善工作单信息。

这里写图片描述

5.1.1. easy ui 提供 datagrid的 行编辑效果

使用 datagrid 的列属性

这里写图片描述

步骤一: 在可编辑 列信息上,添加editor 属性

这里写图片描述

步骤二: 如果想编辑数据,开启某行数据编辑的状态

这里写图片描述

开启编辑

$('#grid').datagrid('beginEdit',1);// 索引1 代表第二行 

结束编辑

$('#grid').datagrid('endEdit',1);// 索引1 代表第二行

取消编辑 (将该行数据,还原到编辑之前的效果 )

$('#grid').datagrid('cancelEdit',1);// 索引1 代表第二行

步骤三: 如何向表格插入新的一行
Easy ui 提供 appendRow 和 insertRow 用于表格数据插入
appendRow 在表格末尾追加一行
insertRow 在指定位置插入一行

在表格第一行,插入一个空白行 :

$('#grid').datagrid('insertRow',{
    index : 0 , // 在第一行进行插入
    row : {}
});

步骤四: 在编辑结束后 或者 取消编辑后,执行事件函数

这里写图片描述

在行编辑后,onAfterEditor 事件函数就会执行,在取消编辑后 onCancelEdit 会执行

onAfterEdit : function(rowIndex, rowData, changes){
    alert('编辑完成');
}
5.1.2. 工作单快速录入页面
/WEB-INF/pages/qupai/quickworkorder.jsp 
// 点击新增一行
function doAdd(){
    // 判断当前是否正在编辑 
    if(editIndex != undefined){
        $("#grid").datagrid('endEdit',editIndex); // 结束当前行编辑
        // 触发onAfterEdit 函数
    }
    // 判断当前已经没有编辑行
    if(editIndex==undefined){ 
        // 在数据表格第一行 ,插入一个空行
        $("#grid").datagrid('insertRow',{
            index : 0,
            row : {}
        });
        // 打开第一行编辑状态
        $("#grid").datagrid('beginEdit',0);
        // 将编辑的行号,保存成员变量
        editIndex = 0;
    }
}

datagrid 行编辑,如果该行数据项不符合form的校验,无法执行 endEdit方法

在datagrid的每行结束编辑时,触发doAfterEdit 事件,在事件中提交ajax请求,提交结束编辑行数据给服务器端,完成数据保存

onAfterEdit : function(rowIndex, rowData, changes){
editIndex = undefined; // 将当前正在编辑行 设置undefined
// 提交ajax请求,将编辑行数据,以ajax方式,发送到服务器,完成保   
$.post("${pageContext.request.contextPath}/workordermanage_saveOrUpdate.action",rowData , function(data){
                });
}

编写服务器代码

public class WorkOrderManageAction extends BaseAction implements ModelDriven<WorkOrderManage> {
}
public interface WorkOrderManageService {
}
public class WorkOrderManageServiceImpl extends BaseService implements WorkOrderManageService {
}

将DAO 注入 BaseService
将Service 注入BaseAction

Action 处理ajax返回结果

这里写图片描述

配置struts.xml 拆分

<include file="struts-user.xml"></include>
<include file="struts-bc.xml"></include> 
<include file="struts-qp.xml"></include>

在 struts-qp.xml 配置工作单

<!-- 工作单管理 -->
<action name="workordermanage_*" class="workOrderManageAction" method="{1}">
    <!-- 保存 -->
    <result name="saveOrUpdateSUCCESS" type="json">
        <param name="root">map</param>
    </result>
</action>

5.2. 工作单快速录入 分页列表查询

为datagrid 添加url

url :  "${pageContext.request.contextPath}/workordermanage_pageQuery.action",

在服务器 WorkOrderManageAction 添加 pageQuery 方法

<!-- 分页查询 -->
<result name="pageQuerySUCCESS" type="json">
    <param name="root">pageResponseBean</param>
    <param name="includeProperties">
        total,
        rows\[\d+\]\.id,
        rows\[\d+\]\.arrivecity,
        rows\[\d+\]\.product,
        rows\[\d+\]\.num,
        rows\[\d+\]\.weight,
        rows\[\d+\]\.floadreqr
    </param>
</result>

5.3. 工作单搜索功能

对工作单使用 like模糊查询时,实际上 数据库内部索引无法使用 ,需要逐条比较查询内容,效率比较低
在数据量很多情况下, 提供模糊查询性能,使用lucene全文索引库技术
—- 最适合 场景 : 论坛和贴吧

Lucene检索原理, 会针对目标内容,先进行分词建立全文索引 ,在用户查找时,先查询索引库中词条,根据词条找到数据记录id ,再根据id 查找数据库记录 !

5.3.1. 在添加工作单,为工作单数据建立索引

直接引入框架 Hibernate Search (用来整合 Hibernate + Lucene)

HibernateSearch是在apacheLucene的基础上建立的主要用于Hibernate的持久化模型的全文检索工具。

1、 在项目导入jar包
导入 hibernate 的jar 、 导入 lucene的jar 、导入hibernate search的jar

这里写图片描述

dist 存放jar包
docs 文档

project 项目源码
hibernate-search-3.4.2.Final.jar : hibernate Search 核心jar包
hibernate-core-3.6.10.Final.jar : hibernate 核心jar包
lucene-core-3.1.0.jar : lucene核心jar包

使用maven坐标 导入 hibernate search

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-search</artifactId>
    <version>3.4.2.Final</version>
</dependency>

2、 导入 IK 分词器
将 IKAnalyzer2012_u6.jar 导入项目
将 IKAnalyzer.cfg.xml、stopword.dic 两个文件 复制 classpath

将jar复制到 项目src/main/webapp/WEB-INF/lib 下

<!-- 导入 IK分词器 -->
<dependency>
    <groupId>ik</groupId>
    <artifactId>ik</artifactId>
    <version>1.0</version>      <systemPath>D:\work\sh_javaee20130731\mavenbos\src\main\webapp\WEB-INF\lib\IKAnalyzer2012_u6.jar</systemPath>
    <scope>system</scope>
</dependency>

3、 配置hibernate search
在applicationContext-common.xml 中配置 索引库位置

<prop key="hibernate.search.default.indexBase">d:/index</prop>

在实体类上面使用注解配置对哪些数据建立索引(只支持注解)

这里写图片描述

自动完成索引库 创建、 更新、删除 !!!!

使用 Luke 工具,查询索引文件内容 !!

这里写图片描述

5.3.2. 结合lucene索引库完成模糊查找功能

使用 EasyUI 提供 searchbox 完成搜索框制作

<!--
     prompt 默认提示内容
     menu 搜索条件下拉选项 
     searcher 点击搜索按钮执行js函数名称
 -->
<input id="ss" class="easyui-searchbox" style="width:300px" 
    data-options="prompt:'请输入您的查询内容',menu:'#mm',searcher:doSearch"/>

<div id="mm">
    <div data-options="name:'arrivecity'">按照到达地搜索</div>
    <div data-options="name:'product'">按照货物名称搜索</div>
</div>

// 搜索函数
function doSearch(value,name){
alert("搜索项:"+name + ", 搜索内容:" + value);
}

将搜索内容,提交给服务器

// 将查询条件 缓存到 datagrid
$('#grid').datagrid('load',{
    conditionName : name,
    conditionValue : value
});

修改 WorkOrderManageAction的 pageQuery 方法,结合条件查询

在GenericDAO 添加查询lucene索引库的方法

/**
 * 结合lucene索引库 进行分页查询
 * 
 * @param conditionName
 * @param conditionValue
 * @param page
 * @param rows
 * @return
 */
public PageResponseBean queryByLucene(String conditionName, String conditionValue, int page, int rows);

这里写图片描述

其它

课前资料

这里写图片描述

hibernate search使用

这里写图片描述

课后资料

这里写图片描述

课程视频内容

这里写图片描述

这里写图片描述

二、完善其余功能节点 -- 流程 1、 将 提供其余节点页面 复制 pages/zhongzhuan 入库(点击左侧菜单入库) ----- Action (查询入库任务列表)---- instore_list.jsp ---- 办理任务 ---- instore_complete.jsp(提交form) --- Action (办理任务流转到下一个节点) 修改function 数据表 入库 task_findInStoreTask.action 出库 task_findOutStoreTask.action 签收 task_findReceiveInfoTask.action 2、 查询任务列表 TaskAction // 业务方法 ----- 查询入库环节 的个人任务 public String findInStoreTask(){ // 登陆用户 User user = (User) getSession().getAttribute("user"); List<ZhongZhuanInfo> zhongZhuanInfoList = myTaskService.findTransferTask(user,"入库"); // 值栈 ActionContext.getContext().put("zhongZhuanInfoList", zhongZhuanInfoList); return "findInStoreTaskSUCCESS"; } // 业务方法 ----- 查询出库环节 的个人任务 public String findOutStoreTask(){ // 登陆用户 User user = (User) getSession().getAttribute("user"); List<ZhongZhuanInfo> zhongZhuanInfoList = myTaskService.findTransferTask(user,"出库"); // 值栈 ActionContext.getContext().put("zhongZhuanInfoList", zhongZhuanInfoList); return "findOutStoreTaskSUCCESS"; } // 业务方法 ----- 查询签收环节 的个人任务 public String findReceiveInfoTask(){ // 登陆用户 User user = (User) getSession().getAttribute("user"); List<ZhongZhuanInfo> zhongZhuanInfoList = myTaskService.findTransferTask(user,"配送签收"); // 值栈 ActionContext.getContext().put("zhongZhuanInfoList", zhongZhuanInfoList); return "findReceiveInfoTaskSUCCESS"; } 3、 配置struts.xml结果页面 <result name="findInStoreTaskSUCCESS">/WEB-INF/pages/zhongzhuan/instore_list.jsp</result> <result name="findOutStoreTaskSUCCESS">/WEB-INF/pages/zhongzhuan/outstore_list.jsp</result> <result name="findReceiveInfoTaskSUCCESS">/WEB-INF/pages/zhongzhuan/receiveinfo_list.jsp</result> 4、 Action办理对应节点任务 // 业务方法 ---- 办理入库任务 public String instorecomplete(){ InStore inStore = new InStore(); inStore.setInfo(info); inStore.setUpdateTime(new Date()); // 调用业务层 myTaskService.completeInStore(taskId, inStore); return "instorecompleteSUCCESS"; } // 业务方法 ---- 办理出库任务 public String outstorecomplete(){ OutStore outStore = new OutStore(); outStore.setInfo(info); outStore.setUpdateTime(new Date()); // 调用业务层 myTaskService.completeOutStore(taskId, outStore); return "outstorecompleteSUCCESS"; } // 业务方法 ---- 办理签收任务 public String receiveinfocomplete(){ ReceiveGoodsInfo receiveGoodsInfo = new ReceiveGoodsInfo(); receiveGoodsInfo.setInfo(info); receiveGoodsInfo.setUpdateTime(new Date()); // 调用业务层 myTaskService.completeReceiveGoodsInfo(taskId, receiveGoodsInfo); return "receiveinfocompleteSUCCESS"; } 5、 struts.xml 跳转回任务列表 <result name="instorecompleteSUCCESS" type="redirectAction">task_findInStoreTask</result> <result name="outstorecompleteSUCCESS" type="redirectAction">task_findOutStoreTask</result> <result name="receiveinfocompleteSUCCESS" type="redirectAction">task_findReceiveInfoTask</result> 在JBPM在流程结束时,发生异常 org.springframework.dao.DataIntegrityViolationException: could not delete: [org.jbpm.pvm.internal.model.ExecutionImpl#50001]; SQL [delete from JBPM4_EXECUTION where DBID_=? and DBVERSION_=?]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not delete: [org.jbpm.pvm.internal.model.ExecutionImpl#50001] 解决: hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect ======================================================================================================================== 三、 流程实例 查看管理 1、 查询实例功能 查询系统正在运行实例 ExecutionService.createProcessInstanceQuery() 查询系统已经完成的实例 HistoryService.createHistoryProcessInstanceQuery() json/admin/json { id:1005, pId:100, name:"查看正在运行的任务", t:"",page:"page_workflow_processinstance.action"} 改为 { id:1005, pId:100, name:"查看正在运行的任务", t:"",page:"processinstance_list.action"} 2、 每个运行流程实例,关联流程变量 ZhongZhuanInfo 包含所有流程信息 根据流程实例id 查询ZhongZhuanInfo数据 (ZhongZhuanInfo) processEngine.getExecutionService().getVariable(pid, "zhongZhuanInfo"); 服务器返回 中转信息 result = zhongZhuanInfo.toString(); <result name="showInfoSUCCESS" type="json"> <param name="root">result</param> </result> 在页面抓中转信息,回显 $.post("${pageContext.request.cotnextPath}/processinstance_showInfo.action", {"pid": pid}, function(data){ $.messager.alert("流程实例信息", data, "info"); }); 3、 实例运行的流程图查看 需要在流程图上面 标记每个 流程运行到哪个任务节点 第一步: 为每条实例记录,添加查看流程图函数 <a href="#" class="easyui-linkbutton" onclick="showPng('<s:property value="#processInstance.id"/>');">查看流程图</a> function showPng(pid){ alert("查看" + pid + "对应流程图"); } 第二步: 流程图查看Action 可以复用 ---- processdefinition_showpng.action?deploymentId= & resourceName= RepositoryService.getResourceAsStream(java.lang.String deploymentId, java.lang.String resourceName) * resourceName 可以通过 deploymentId 动态获得 RepositoryService repositoryService = processEngine.getRepositoryService(); ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().deploymentId(deploymentId).uniqueResult(); resourceName = processDefinition.getImageResourceName(); 第三步: 点击查看流程图, 获得活动节点坐标 通过 window.showModalDialog() 查看流程图页面 (弹出窗口,显示页面 url地址不能修改 ) function showPng(pid){ //alert("查看" + pid + "对应流程图"); window.showModalDialog("${pageContext.request.contextPath}/processinstance_findactivityinfo.action?pid="+pid); } 查看某一个具体活动节点坐标 String processDefinitionId = "test-1"; // 流程定义的id String activityName = "start1"; // 活动的名称 ActivityCoordinates c = processEngine.getRepositoryService() .getActivityCoordinates(processDefinitionId, activityName); 查看当前流程实例活动所有节点名称 ProcessInstance的 java.util.Set<java.lang.String> findActiveActivityNames() @Override public List<ActivityCoordinates> findActivityCoordinates(String pid) { // 1、 根据流程实例id 获得所有活动节点名称 ProcessInstance processInstance = processEngine.getExecutionService().createProcessInstanceQuery().processInstanceId(pid).uniqueResult(); Set<String> activityNames = processInstance.findActiveActivityNames(); // 2、一个活动节点 --- 对应一个坐标对象 List<ActivityCoordinates> activityCoordinates = new ArrayList<ActivityCoordinates>(); for(String activityName: activityNames){ // 获得每一个活动节点名称 String processDefinitionId = processInstance.getProcessDefinitionId();//流程定义id ActivityCoordinates activityCoordinate = processEngine.getRepositoryService().getActivityCoordinates(processDefinitionId, activityName); activityCoordinates.add(activityCoordinate); } return activityCoordinates; } ============ 为了在下一个页面 可以显示流程图, 根据实例id 查询 发布id @Override public String findDeploymentIdByProcessInstanceId(String pid) { ProcessInstance processInstance = processEngine.getExecutionService().createProcessInstanceQuery().processInstanceId(pid).uniqueResult(); ProcessDefinition processDefinition = processEngine.getRepositoryService().createProcessDefinitionQuery().processDefinitionId(processInstance.getProcessDefinitionId()).uniqueResult(); return processDefinition.getDeploymentId(); } <img src="${pageContext.request.contextPath }/processdefinition_showpng.action?deploymentId=<s:property value="#deploymentId"/>" style="position: absolute;top: 0;left: 0"/> <s:iterator value="#activityCoordinates" var="activityCoordinate"> <div style="width: <s:property value="#activityCoordinate.width"/>px; height: <s:property value="#activityCoordinate.height"/>px; left: <s:property value="#activityCoordinate.x"/>px; top: <s:property value="#activityCoordinate.y"/>px; position: absolute; border-color: red; border-style: solid; border-width: 1px;"> </s:iterator>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值