Merge remote-tracking branch 'origin/dev' into dev
commit
91d91e0712
|
@ -1,4 +1,33 @@
|
||||||
(function runJs() {
|
(function runJs() {
|
||||||
|
function findReact(dom, traverseUp = 0) {
|
||||||
|
const key = Object.keys(dom).find(key => {
|
||||||
|
return key.startsWith("__reactFiber$")
|
||||||
|
|| key.startsWith("__reactInternalInstance$")
|
||||||
|
|| key.startsWith("__reactEventHandlers$");
|
||||||
|
});
|
||||||
|
const domFiber = dom[key];
|
||||||
|
if (domFiber == null) return null;
|
||||||
|
if (domFiber._currentElement) {
|
||||||
|
let compFiber = domFiber._currentElement._owner;
|
||||||
|
for (let i = 0; i < traverseUp; i++) {
|
||||||
|
compFiber = compFiber._currentElement._owner;
|
||||||
|
}
|
||||||
|
return compFiber._instance;
|
||||||
|
}
|
||||||
|
const GetCompFiber = fiber => {
|
||||||
|
let parentFiber = fiber.return;
|
||||||
|
while (typeof parentFiber.type == "string") {
|
||||||
|
parentFiber = parentFiber.return;
|
||||||
|
}
|
||||||
|
return parentFiber;
|
||||||
|
};
|
||||||
|
let compFiber = GetCompFiber(domFiber);
|
||||||
|
for (let i = 0; i < traverseUp; i++) {
|
||||||
|
compFiber = GetCompFiber(compFiber);
|
||||||
|
}
|
||||||
|
return compFiber.stateNode;
|
||||||
|
}
|
||||||
|
|
||||||
let currentYear = new Date().getFullYear();
|
let currentYear = new Date().getFullYear();
|
||||||
let currentMonth = new Date().getMonth() + 1;
|
let currentMonth = new Date().getMonth() + 1;
|
||||||
let currentDate = new Date().getDate();
|
let currentDate = new Date().getDate();
|
||||||
|
@ -23,9 +52,13 @@
|
||||||
wf.changeFieldValue("100003720000000611", {value: "外出技术支持"});
|
wf.changeFieldValue("100003720000000611", {value: "外出技术支持"});
|
||||||
wf.changeFieldValue("100003720000008715", {value: '2'});
|
wf.changeFieldValue("100003720000008715", {value: '2'});
|
||||||
wf.changeFieldValue("877132351682273302", {value: '1'});
|
wf.changeFieldValue("877132351682273302", {value: '1'});
|
||||||
let workflowTitleObj = document.querySelector('input[weid="3rdcst_oxa9w7_i8bbvp_vc1wev_kc1m3l_r1vh81_snhw9p_3r9w93_g28s4n_abfe5k_a9abii"]');
|
let workflowTitleObj = document.querySelector('.weapp-form-flowpage-content-FlowPageSysFieldReqName input');
|
||||||
let workflowTitle = workflowTitleObj.value;
|
let workflowTitle = workflowTitleObj.value;
|
||||||
workflowTitleObj.value = workflowTitle.substring(0, workflowTitle.length - 5) + formattedMonth + "-" + formattedDay;
|
let newValue = workflowTitle.substring(0, workflowTitle.length - 5) + formattedMonth + "-" + formattedDay;
|
||||||
|
workflowTitleObj.setAttribute("value", newValue);
|
||||||
|
workflowTitleObj.value = newValue;
|
||||||
|
let input = findReact(workflowTitleObj);
|
||||||
|
input.props.onChange(newValue);
|
||||||
const container = document.getElementById("widget_100003720000000664");
|
const container = document.getElementById("widget_100003720000000664");
|
||||||
const svgIcon = container.querySelector(".associative-search-icon svg");
|
const svgIcon = container.querySelector(".associative-search-icon svg");
|
||||||
if (svgIcon) {
|
if (svgIcon) {
|
||||||
|
@ -43,6 +76,35 @@
|
||||||
})()
|
})()
|
||||||
|
|
||||||
(function runJs() {
|
(function runJs() {
|
||||||
|
function findReact(dom, traverseUp = 0) {
|
||||||
|
const key = Object.keys(dom).find(key => {
|
||||||
|
return key.startsWith("__reactFiber$")
|
||||||
|
|| key.startsWith("__reactInternalInstance$")
|
||||||
|
|| key.startsWith("__reactEventHandlers$");
|
||||||
|
});
|
||||||
|
const domFiber = dom[key];
|
||||||
|
if (domFiber == null) return null;
|
||||||
|
if (domFiber._currentElement) {
|
||||||
|
let compFiber = domFiber._currentElement._owner;
|
||||||
|
for (let i = 0; i < traverseUp; i++) {
|
||||||
|
compFiber = compFiber._currentElement._owner;
|
||||||
|
}
|
||||||
|
return compFiber._instance;
|
||||||
|
}
|
||||||
|
const GetCompFiber = fiber => {
|
||||||
|
let parentFiber = fiber.return;
|
||||||
|
while (typeof parentFiber.type == "string") {
|
||||||
|
parentFiber = parentFiber.return;
|
||||||
|
}
|
||||||
|
return parentFiber;
|
||||||
|
};
|
||||||
|
let compFiber = GetCompFiber(domFiber);
|
||||||
|
for (let i = 0; i < traverseUp; i++) {
|
||||||
|
compFiber = GetCompFiber(compFiber);
|
||||||
|
}
|
||||||
|
return compFiber.stateNode;
|
||||||
|
}
|
||||||
|
|
||||||
let currentYear = new Date().getFullYear();
|
let currentYear = new Date().getFullYear();
|
||||||
let currentMonth = new Date().getMonth() + 1;
|
let currentMonth = new Date().getMonth() + 1;
|
||||||
let currentDate = new Date().getDate();
|
let currentDate = new Date().getDate();
|
||||||
|
@ -62,9 +124,13 @@
|
||||||
wf.changeFieldValue("100003720000000611", {value: "外出技术支持"});
|
wf.changeFieldValue("100003720000000611", {value: "外出技术支持"});
|
||||||
wf.changeFieldValue("100003720000008715", {value: '4'});
|
wf.changeFieldValue("100003720000008715", {value: '4'});
|
||||||
wf.changeFieldValue("877132351682273302", {value: '1'});
|
wf.changeFieldValue("877132351682273302", {value: '1'});
|
||||||
let workflowTitleObj = document.querySelector('input[weid="3rdcst_oxa9w7_i8bbvp_vc1wev_kc1m3l_r1vh81_snhw9p_3r9w93_g28s4n_abfe5k_a9abii"]');
|
let workflowTitleObj = document.querySelector('.weapp-form-flowpage-content-FlowPageSysFieldReqName input');
|
||||||
let workflowTitle = workflowTitleObj.value;
|
let workflowTitle = workflowTitleObj.value;
|
||||||
workflowTitleObj.value = workflowTitle.substring(0, workflowTitle.length - 5) + formattedMonth + "-" + formattedDay;
|
let newValue = workflowTitle.substring(0, workflowTitle.length - 5) + formattedMonth + "-" + formattedDay;
|
||||||
|
workflowTitleObj.setAttribute("value", newValue);
|
||||||
|
workflowTitleObj.value = newValue;
|
||||||
|
let input = findReact(workflowTitleObj);
|
||||||
|
input.props.onChange(newValue);
|
||||||
window.open("/sp/workflow/flowpage/fullCreate/100003460000000746?workflowId=100003460000000746&isCreate=1", "_blank");
|
window.open("/sp/workflow/flowpage/fullCreate/100003460000000746?workflowId=100003460000000746&isCreate=1", "_blank");
|
||||||
document.querySelector("button[weid='3rdcst_oxa9w7_i8bbvp_vc1wev_kc1m3l_r1vh81_t03ihg@0_xomsa1@0']").click();
|
document.querySelector("button[weid='3rdcst_oxa9w7_i8bbvp_vc1wev_kc1m3l_r1vh81_t03ihg@0_xomsa1@0']").click();
|
||||||
})()
|
})()
|
|
@ -56,36 +56,61 @@ public class ResponseMappingDeal {
|
||||||
this.doResponseSync(uniqueCode, responseMap);
|
this.doResponseSync(uniqueCode, responseMap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>响应同步 note by youhong.ai</h2>
|
||||||
|
*
|
||||||
|
* @param uniqueCode 唯一表示
|
||||||
|
* @param responseMap 响应map
|
||||||
|
*/
|
||||||
public void doResponseSync(String uniqueCode, Map<String, Object> responseMap) {
|
public void doResponseSync(String uniqueCode, Map<String, Object> responseMap) {
|
||||||
|
// note by youhong.ai 查询配置表
|
||||||
List<ResponseConfig> responseConfigList = configMapper.queryResponseConfigByUnique(uniqueCode);
|
List<ResponseConfig> responseConfigList = configMapper.queryResponseConfigByUnique(uniqueCode);
|
||||||
logger.info(String.format("%s 相关响应配置信息==> %s", uniqueCode, JSON.toJSONString(responseConfigList)));
|
logger.info(String.format("%s 相关响应配置信息==> %s", uniqueCode, JSON.toJSONString(responseConfigList)));
|
||||||
|
// note by youhong.ai 自定义校验
|
||||||
ResponseUtil.parameterJudgment(responseConfigList, "response config is empty please check!!! ");
|
ResponseUtil.parameterJudgment(responseConfigList, "response config is empty please check!!! ");
|
||||||
Map<String, ResponseConfig> tableNameConfig;
|
Map<String, ResponseConfig> tableNameConfig;
|
||||||
try {
|
try {
|
||||||
|
// note by youhong.ai 对查询到的多个配置进行整合,以同步表表名作为key 配置作为value
|
||||||
tableNameConfig = responseConfigList.stream().collect(Collectors.toMap(ResponseConfig::getModelTableName, v -> v));
|
tableNameConfig = responseConfigList.stream().collect(Collectors.toMap(ResponseConfig::getModelTableName, v -> v));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
logger.error("response config error please check!!! " + Util.getErrString(e));
|
logger.error("response config error please check!!! " + Util.getErrString(e));
|
||||||
throw new ResponseException("response config error please check!!! ");
|
throw new ResponseException("response config error please check!!! ");
|
||||||
}
|
}
|
||||||
ResponseUtil.parameterJudgment(responseMap, "接口返回都为空 你处理个勾");
|
ResponseUtil.parameterJudgment(responseMap, "接口返回都为空 你处理个勾");
|
||||||
|
// note by youhong.ai 循环同步每一个表
|
||||||
tableNameConfig.forEach((key, value) -> doResponseSync(value, responseMap));
|
tableNameConfig.forEach((key, value) -> doResponseSync(value, responseMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>note by youhong.ai 同步数据</h2>
|
||||||
|
*
|
||||||
|
* @param responseConfig 配置表
|
||||||
|
* @param responseMap 响应结果
|
||||||
|
*/
|
||||||
public void doResponseSync(ResponseConfig responseConfig, Map<String, Object> responseMap) {
|
public void doResponseSync(ResponseConfig responseConfig, Map<String, Object> responseMap) {
|
||||||
|
// note by youhong.ai 获取别名配置明细数据
|
||||||
List<ResponseConfigAlias> responseConfigAliasList = responseConfig.getResponseConfigAliasList();
|
List<ResponseConfigAlias> responseConfigAliasList = responseConfig.getResponseConfigAliasList();
|
||||||
ResponseUtil.parameterJudgment(responseConfigAliasList, "responseConfigAliasList config is empty please check!!!");
|
ResponseUtil.parameterJudgment(responseConfigAliasList, "responseConfigAliasList config is empty please check!!!");
|
||||||
|
// note by youhong.ai 获取转换值配置明细
|
||||||
List<ResponseConfigValueChange> valueChangeList = responseConfig.getValueChangeList();
|
List<ResponseConfigValueChange> valueChangeList = responseConfig.getValueChangeList();
|
||||||
ResponseUtil.parameterJudgment(valueChangeList, "valueChangeList config is empty please check!!!");
|
ResponseUtil.parameterJudgment(valueChangeList, "valueChangeList config is empty please check!!!");
|
||||||
// 数据信息按是否主表分组
|
// 数据信息按是否主表分组
|
||||||
Map<Integer, List<ResponseConfigAlias>> aliasMap = responseConfigAliasList.stream().collect(Collectors.groupingBy(ResponseConfigAlias::getTableType));
|
Map<Integer, List<ResponseConfigAlias>> aliasMap = responseConfigAliasList.stream().collect(Collectors.groupingBy(ResponseConfigAlias::getTableType));
|
||||||
|
// note by youhong.ai 获取主表别名配置数据
|
||||||
List<ResponseConfigAlias> mainConfigList = aliasMap.get(ResponseConfigConstant.MAIN_TABLE);
|
List<ResponseConfigAlias> mainConfigList = aliasMap.get(ResponseConfigConstant.MAIN_TABLE);
|
||||||
|
// note by youhong.ai 按照表类型分组
|
||||||
Map<Integer, List<ResponseConfigValueChange>> mainOrDetail = valueChangeList.stream().collect(Collectors.groupingBy(ResponseConfigValueChange::getTableType));
|
Map<Integer, List<ResponseConfigValueChange>> mainOrDetail = valueChangeList.stream().collect(Collectors.groupingBy(ResponseConfigValueChange::getTableType));
|
||||||
|
// note by youhong.ai 获取主表值配置
|
||||||
List<ResponseConfigValueChange> mainValueChangeList = mainOrDetail.get(ResponseConfigConstant.MAIN_TABLE);
|
List<ResponseConfigValueChange> mainValueChangeList = mainOrDetail.get(ResponseConfigConstant.MAIN_TABLE);
|
||||||
TableDefinition tableDefinition;
|
TableDefinition tableDefinition;
|
||||||
|
// note by youhong.ai 如果有主表配置
|
||||||
if (ResponseUtil.parameterIsNotNull(mainConfigList)) {
|
if (ResponseUtil.parameterIsNotNull(mainConfigList)) {
|
||||||
|
// note by youhong.ai 解析json为表描述对象
|
||||||
tableDefinition = this.parsingJsonToTable(responseConfig.getModelTableName(), responseConfig, mainConfigList, mainValueChangeList, responseMap, true, this::detailTableDeal);
|
tableDefinition = this.parsingJsonToTable(responseConfig.getModelTableName(), responseConfig, mainConfigList, mainValueChangeList, responseMap, true, this::detailTableDeal);
|
||||||
} else {
|
} else {
|
||||||
|
// note by youhong.ai 如果没有主表配置,纯配置明细同步
|
||||||
Map<String, TableDefinition> detailTable = new HashMap<>();
|
Map<String, TableDefinition> detailTable = new HashMap<>();
|
||||||
|
// note by youhong.ai 处理明细表
|
||||||
this.detailTableDeal(responseConfig, responseMap, detailTable);
|
this.detailTableDeal(responseConfig, responseMap, detailTable);
|
||||||
tableDefinition = TableDefinition.builder()
|
tableDefinition = TableDefinition.builder()
|
||||||
.detailTableMap(detailTable)
|
.detailTableMap(detailTable)
|
||||||
|
@ -99,6 +124,18 @@ public class ResponseMappingDeal {
|
||||||
tableDefinition.dataProcess();
|
tableDefinition.dataProcess();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <h2>note by youhon.ai 解析json为表描述对象</h2>
|
||||||
|
*
|
||||||
|
* @param tableName 表名
|
||||||
|
* @param responseConfig 响应配置
|
||||||
|
* @param responseConfigAliasList 别名配置
|
||||||
|
* @param valueChangeList 值同步配置
|
||||||
|
* @param responseMap 响应结果
|
||||||
|
* @param isMainTable 是否主表
|
||||||
|
* @param detailCallBack 明细处理回调
|
||||||
|
* @return 表描述
|
||||||
|
*/
|
||||||
public TableDefinition parsingJsonToTable(
|
public TableDefinition parsingJsonToTable(
|
||||||
String tableName,
|
String tableName,
|
||||||
ResponseConfig responseConfig,
|
ResponseConfig responseConfig,
|
||||||
|
@ -107,8 +144,9 @@ public class ResponseMappingDeal {
|
||||||
Map<String, Object> responseMap,
|
Map<String, Object> responseMap,
|
||||||
boolean isMainTable,
|
boolean isMainTable,
|
||||||
Bi3Function<ResponseConfig, Map<String, Object>, Map<String, TableDefinition>, String> detailCallBack) {
|
Bi3Function<ResponseConfig, Map<String, Object>, Map<String, TableDefinition>, String> detailCallBack) {
|
||||||
|
// note by youhong.ai 过滤出主表别名主数据配置
|
||||||
List<ResponseConfigAlias> mainAliasList = responseConfigAliasList.stream().filter(item -> item.getMainData() == ResponseConfigConstant.MAIN_DATA).collect(Collectors.toList());
|
List<ResponseConfigAlias> mainAliasList = responseConfigAliasList.stream().filter(item -> item.getMainData() == ResponseConfigConstant.MAIN_DATA).collect(Collectors.toList());
|
||||||
|
// note by youhong.ai 主表别名有且只能有一个主数据
|
||||||
if (responseConfigAliasList.size() > 1) {
|
if (responseConfigAliasList.size() > 1) {
|
||||||
throw new ResponseException("The master data cannot be multiple please check !!! ");
|
throw new ResponseException("The master data cannot be multiple please check !!! ");
|
||||||
}
|
}
|
||||||
|
@ -120,7 +158,9 @@ public class ResponseMappingDeal {
|
||||||
responseConfigAliasList.forEach(item -> {
|
responseConfigAliasList.forEach(item -> {
|
||||||
String dataPath = item.getDataPath();
|
String dataPath = item.getDataPath();
|
||||||
String dataAlias = item.getDataAlias();
|
String dataAlias = item.getDataAlias();
|
||||||
|
// node by youhong.ai 通过路径获取对应的值
|
||||||
Object parsingData = Util.getValueByKeyStr(dataPath, responseMap);
|
Object parsingData = Util.getValueByKeyStr(dataPath, responseMap);
|
||||||
|
// node by youhong.ai 别名->值
|
||||||
aliasData.put(dataAlias, parsingData);
|
aliasData.put(dataAlias, parsingData);
|
||||||
});
|
});
|
||||||
// Map<Integer, List<ResponseConfigValueChange>> mainOrDetail = valueChangeList.stream().collect(Collectors.groupingBy(ResponseConfigValueChange::getTableType));
|
// Map<Integer, List<ResponseConfigValueChange>> mainOrDetail = valueChangeList.stream().collect(Collectors.groupingBy(ResponseConfigValueChange::getTableType));
|
||||||
|
@ -140,6 +180,7 @@ public class ResponseMappingDeal {
|
||||||
String dataPath = mainDataAlias.getDataPath();
|
String dataPath = mainDataAlias.getDataPath();
|
||||||
List<RowDefinition> rowDefinitionList = new ArrayList<>();
|
List<RowDefinition> rowDefinitionList = new ArrayList<>();
|
||||||
Map<String, TableDefinition> detailTable = isMainTable ? new HashMap<>() : null;
|
Map<String, TableDefinition> detailTable = isMainTable ? new HashMap<>() : null;
|
||||||
|
// note by youhong.ai 如果是json数组
|
||||||
if (mainDataAlias.getDataType() == ResponseConfigConstant.JSON_ARRAY) {
|
if (mainDataAlias.getDataType() == ResponseConfigConstant.JSON_ARRAY) {
|
||||||
List<Map<String, Object>> mainList = (List<Map<String, Object>>) Util.getValueByKeyStr(dataPath, aliasData);
|
List<Map<String, Object>> mainList = (List<Map<String, Object>>) Util.getValueByKeyStr(dataPath, aliasData);
|
||||||
for (Map<String, Object> mainItem : mainList) {
|
for (Map<String, Object> mainItem : mainList) {
|
||||||
|
@ -224,6 +265,7 @@ public class ResponseMappingDeal {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将json参数解析成对应的字段描述信息
|
* 将json参数解析成对应的字段描述信息
|
||||||
|
*
|
||||||
* @param responseConfigValueChangeList 配置信息
|
* @param responseConfigValueChangeList 配置信息
|
||||||
* @param parsingData json信息
|
* @param parsingData json信息
|
||||||
* @return 字段描述信息
|
* @return 字段描述信息
|
||||||
|
|
|
@ -27,7 +27,6 @@ import java.util.Objects;
|
||||||
public class SsoGuoXiaoJunPocImpl {
|
public class SsoGuoXiaoJunPocImpl {
|
||||||
|
|
||||||
public static final String API_SSO_URL = "/api/aiyh/guoxiaojun/sso";
|
public static final String API_SSO_URL = "/api/aiyh/guoxiaojun/sso";
|
||||||
public static final String LOGIN_URL = "/wui/index.html";
|
|
||||||
public static final String API_CHECK_OUT_URI = "/api/hrm/login/checkLogout";
|
public static final String API_CHECK_OUT_URI = "/api/hrm/login/checkLogout";
|
||||||
private final Logger log = Util.getLogger();
|
private final Logger log = Util.getLogger();
|
||||||
|
|
||||||
|
@ -55,20 +54,24 @@ public class SsoGuoXiaoJunPocImpl {
|
||||||
if (requestURI.equals(API_CHECK_OUT_URI)) {
|
if (requestURI.equals(API_CHECK_OUT_URI)) {
|
||||||
// 退出登录
|
// 退出登录
|
||||||
session.removeAttribute("outUserId");
|
session.removeAttribute("outUserId");
|
||||||
String logOutUrl = Util.getCusConfigValue("oauth2-proxy-logout-url");
|
|
||||||
response.setHeader("logOutUrl", logOutUrl);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// sso 认证
|
// sso 认证
|
||||||
if (session.getAttribute("outUserId") == null) {
|
if (session.getAttribute("outUserId") == null) {
|
||||||
authorSso(response, session);
|
authorSso(response);
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
log.error("重定地址失败!" + Util.getErrString(e));
|
log.error("重定地址失败!" + Util.getErrString(e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
/**
|
||||||
|
* <h2>登录oa系统</h2>
|
||||||
|
*
|
||||||
|
* @param request 请求
|
||||||
|
* @param response 响应
|
||||||
|
* @throws IOException 异常
|
||||||
|
*/
|
||||||
private void loginOa(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
private void loginOa(HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||||
HttpSession session;
|
HttpSession session;
|
||||||
String outUserId = request.getHeader("X-Credential-Identifier");
|
String outUserId = request.getHeader("X-Credential-Identifier");
|
||||||
|
@ -84,11 +87,13 @@ public class SsoGuoXiaoJunPocImpl {
|
||||||
response.sendRedirect(Util.getCusConfigValueNullOrEmpty("SSO_SUCCESS_REDIRECT_URL", "/wui/index.html"));
|
response.sendRedirect(Util.getCusConfigValueNullOrEmpty("SSO_SUCCESS_REDIRECT_URL", "/wui/index.html"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void authorSso(HttpServletResponse response, HttpSession session) throws IOException {
|
/**
|
||||||
Object outUserId = session.getAttribute("outUserId");
|
* <h2>跳转认证</h2>
|
||||||
if (Objects.nonNull(outUserId)) {
|
*
|
||||||
return;
|
* @param response 响应
|
||||||
}
|
* @throws IOException 异常
|
||||||
|
*/
|
||||||
|
private void authorSso(HttpServletResponse response) throws IOException {
|
||||||
// 地址重定向到国小君aut2认证地址
|
// 地址重定向到国小君aut2认证地址
|
||||||
response.sendRedirect(Util.getCusConfigValue("oauth2-proxy-url") + "?url="
|
response.sendRedirect(Util.getCusConfigValue("oauth2-proxy-url") + "?url="
|
||||||
+ URLEncoder.encode(Util.getCusConfigValue("OA_URL") + API_SSO_URL, "UTF-8"));
|
+ URLEncoder.encode(Util.getCusConfigValue("OA_URL") + API_SSO_URL, "UTF-8"));
|
||||||
|
|
Loading…
Reference in New Issue