// ==UserScript==
// @name 易用云
// @namespace 让易搭云更易用!好用到爆炸!(云生集团-研发专用版)
// @version 0.1.0
// @match https://web.yidayun.com/*
// @icon https://www.yidayun.com/images/favicon.png
// @grant none
// @author Jack.Chan ([email protected])
// @namespace http://fulicat.com
// @url https://gf.qytechs.cn/zh-CN/scripts/480426
// @license MIT
// @description 2023/11/28 16:37:43
// ==/UserScript==
(function() {
// 忽略注入类型
if (document.contentType !== 'text/html' || (/\w+\.{1,1}\w+/.test(location.pathname) && !/\w+\.{1,1}html/.test(location.pathname) ) || /\w+\.{2,}\w+/.test(location.pathname) ) {
console.warn(`不支持在此类型文档中注入,`, document.contentType);
return false;
}
// html类型 + 非iframe嵌套时 清空 body
if (/\w+\.{1,1}html/.test(location.pathname) && window.top === window) {
document.body.innerHTML = '';
}
// 图标
const ICONS = {
close: `
<svg width="16px" height="16px" viewBox="0 0 10 10" version="1.1">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<path d="M8.95969723,0.897476866 C9.21843425,1.15582278 9.24028631,1.56159861 9.02504921,1.84484482 L8.96046978,1.91885304 L5.91916574,4.96548241 C5.92235682,5.00422322 5.92243302,5.04317194 5.91939467,5.08192184 L8.95969664,8.1196985 C9.21843396,8.37804412 9.24028649,8.78381992 9.02504971,9.06706638 L8.96047037,9.14107467 C8.70212475,9.399812 8.29634895,9.42166452 8.01310249,9.20642774 L7.9390942,9.1418484 L5.01806209,6.22532901 L2.09858553,9.1418484 L2.02457724,9.20642774 C1.74133078,9.42166452 1.33555498,9.399812 1.07720936,9.14107467 L1.07720936,9.14107467 L1.01263002,9.06706638 C0.797393239,8.78381992 0.819245764,8.37804412 1.07798309,8.1196985 L1.07798309,8.1196985 L4.11828506,5.08192184 C4.1152467,5.04317194 4.11532291,5.00422322 4.11851399,4.96548241 L1.07720994,1.91885304 L1.01263052,1.84484482 C0.797393414,1.56159861 0.819245474,1.15582278 1.0779825,0.897476866 C1.36024108,0.615644961 1.81752677,0.615990848 2.09935867,0.898249425 L2.09935867,0.898249425 L5.01906209,3.82132901 L7.93832106,0.898249425 C8.22015296,0.615990848 8.67743865,0.615644961 8.95969723,0.897476866 Z" fill="currentColor" fill-rule="nonzero"></path>
</g>
</svg>
`.trim(),
star: `
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="svg-icon svg-icon-star">
<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Zm0 2.445L6.615 5.5a.75.75 0 0 1-.564.41l-3.097.45 2.24 2.184a.75.75 0 0 1 .216.664l-.528 3.084 2.769-1.456a.75.75 0 0 1 .698 0l2.77 1.456-.53-3.084a.75.75 0 0 1 .216-.664l2.24-2.183-3.096-.45a.75.75 0 0 1-.564-.41L8 2.694Z"></path>
</svg>
`.trim(),
starred: `
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="svg-icon svg-icon-star-fill svg-icon-starred">
<path d="M8 .25a.75.75 0 0 1 .673.418l1.882 3.815 4.21.612a.75.75 0 0 1 .416 1.279l-3.046 2.97.719 4.192a.751.751 0 0 1-1.088.791L8 12.347l-3.766 1.98a.75.75 0 0 1-1.088-.79l.72-4.194L.818 6.374a.75.75 0 0 1 .416-1.28l4.21-.611L7.327.668A.75.75 0 0 1 8 .25Z"></path>
</svg>
`.trim(),
pin: `
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="svg-icon svg-icon-pin">
<path d="m11.294.984 3.722 3.722a1.75 1.75 0 0 1-.504 2.826l-1.327.613a3.089 3.089 0 0 0-1.707 2.084l-.584 2.454c-.317 1.332-1.972 1.8-2.94.832L5.75 11.311 1.78 15.28a.749.749 0 1 1-1.06-1.06l3.969-3.97-2.204-2.204c-.968-.968-.5-2.623.832-2.94l2.454-.584a3.08 3.08 0 0 0 2.084-1.707l.613-1.327a1.75 1.75 0 0 1 2.826-.504ZM6.283 9.723l2.732 2.731a.25.25 0 0 0 .42-.119l.584-2.454a4.586 4.586 0 0 1 2.537-3.098l1.328-.613a.25.25 0 0 0 .072-.404l-3.722-3.722a.25.25 0 0 0-.404.072l-.613 1.328a4.584 4.584 0 0 1-3.098 2.537l-2.454.584a.25.25 0 0 0-.119.42l2.731 2.732Z"></path>
</svg>
`.trim(),
copy: `
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="svg-icon svg-icon-copy">
<path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path>
</svg>
`.trim(),
check: `
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" class="svg-icon svg-icon-check">
<path d="M13.78 4.22a.75.75 0 0 1 0 1.06l-7.25 7.25a.75.75 0 0 1-1.06 0L2.22 9.28a.751.751 0 0 1 .018-1.042.751.751 0 0 1 1.042-.018L6 10.94l6.72-6.72a.75.75 0 0 1 1.06 0Z"></path>
</svg>
`.trim(),
};
String.prototype.trimHTML = function() {
var html = this.trim();
html = html.replace(/<!--[\s\S]*?-->/g, '');
// html = html.replace(/>\s+</g, '><');
html = html.replace(/>\s</g, '><');
html = html.replace(/>\t</g, '><');
html = html.replace(/>\n</g, '><');
html = html.replace(/>\n\t+</g, '><');
return html;
}
const formatDate = (timestamp) => {
if (timestamp) {
return new Date(timestamp).toLocaleString();
}
return '';
}
const copyElementContents = (el) => {
return new Promise((resolve, reject) => {
if (!el) {
return reject(new Error('el not found'));
}
if (this.timer_copy) {
clearTimeout(this.timer_copy);
this.timer_copy = null;
}
var body = document.body, range, sel;
if (document.createRange && window.getSelection) {
range = document.createRange();
sel = window.getSelection();
sel.removeAllRanges();
try {
range.selectNodeContents(el);
sel.addRange(range);
} catch (e) {
range.selectNode(el);
sel.addRange(range);
}
} else if (body.createTextRange) {
range = body.createTextRange();
range.moveToElementText(el);
range.select();
}
document.execCommand('Copy');
this.timer_copy = setTimeout(() => {
window.getSelection().empty();
resolve();
}, 250);
});
}
const renderLinkHref = (data) => {
const view = {
type: 'windowAction',
target: 'tab',
object: data.object,
name: (data.title || (data.project + data.name)).substr(0, 50),
resId: data.resId,
viewNumber: data.viewNumber,
// context: { flowFlag: true }
};
if (data.context) {
view.context = data.context;
}
return `https://web.yidayun.com/home?view=${ encodeURIComponent(JSON.stringify(view)) }`;
};
const renderPlayers = (players) => {
if (!players) {
return '';
}
try {
players = JSON.parse(players);
players = Array.isArray(players) ? players.join('、') : JSON.stringify(players);
} catch (ex) {
// console.warn(ex);
}
return players;
};
const renderField = (value) => {
// console.log('v:', value);
if (value === undefined || value === null) {
value = '';
}
if (typeof value === 'string') {
value = value.trim();
}
return value;
}
const trimObject = (object) => {
if (object === undefined || object === null) {
return '';
}
if (typeof object === 'string') {
return object.trim();
}
if (typeof object === 'object') {
if (Array.isArray(object)) {
return object.map((item) => {
return trimObject(item);
});
}
for (var key in object) {
if (object[key] === undefined || object[key] === null) {
object[key] = '';
} else if (typeof object[key] === 'string') {
object[key] = trimObject(object[key]);
}
}
return object;
}
return object;
}
const doCopy = (text) => {
return new Promise((resolve, reject) => {
if (typeof text !== 'string') {
return resolve('');
}
text = text.trim();
var $text = document.createElement('textarea');
$text.setAttribute('readonly', 'readonly');
$text.style.cssText = 'width: 10px;height: 10px;position: fixed;top:-999px;left:-999px;';
$text.value = text;
document.body.appendChild($text);
$text.select();
if (this.timer_copy) {
clearTimeout(this.timer_copy);
this.timer_copy = null;
}
this.timer_copy = setTimeout(() => {
if (document.execCommand('copy')) {
resolve(text);
} else {
reject(text);
}
this.timer_copy = setTimeout(() => {
this.timer_copy = null;
document.body.removeChild($text);
}, 100);
}, 250);
});
}
// @ request
const $fetch = (url, data, options) => {
options = { method: 'POST', mode: 'cors', credentials: 'omit', ...options };
options.headers = { 'content-type': 'application/json', ...options.headers };
options.headers['Accept'] = 'application/json, text/plain, */*';
options.headers['X-Session'] = document.cookie;
if (data) {
if (options.method.toUpperCase() === 'GET' || options.method.toUpperCase() === 'HEAD') {
url+= (url.indexOf('?') > -1 ? '&' : '?') + (new URLSearchParams(data)).toString();
} else {
options.body = typeof data === 'object' ? JSON.stringify(data) : data;
}
}
return new Promise((resolve, reject) => {
fetch(url, options).then((res) => res.json()).then((res) => {
if (res.errorCode === 0 && res.success) {
resolve(res);
} else {
console.error('[ERR]', url, options, res);
reject(res);
}
}).catch((err) => {
reject(err);
});
});
}
// 用户信息
var USER_INFO = {
data: {},
set(data) {
this.data = data;
},
get(key) {
if(key) {
return this.data[key];
}
return this.data;
}
}
class BTN_SHARE {
get name() {
return '分享按钮';
}
#root = null;
#btnClassName = 'y-app-link-btn-share';
// #btnSelector = '.y-app-link-btn-share';
#btnSelector = '.'+ this.#btnClassName;
constructor() {
console.log(`[OK] 初始化 ${ this.name } ...`);
this.init();
}
getShareLink(params) {
if (typeof params !== 'object' || !params.objectNumber || !params.dataId) {
console.warn(`[WARN] 参数错误`, params);
return Promise.reject('params error');
}
return $fetch('https://api.yidayun.com/share/getShareLink', {
context: {},
params: { objectNumber: params.objectNumber, dataId: params.dataId }
}).then((res) => {
params.url = res.data;
return params;
}).catch((err) => {
console.warn('[WARN]', err);
return err;
});
}
inject($el) {
// $el is current tab
if (!$el) {
return;
}
if ($el.querySelector(this.#btnSelector)) {
return console.warn(`[WARN] 已注入 ${ this.name }`);
}
$el.$btnShare = this.createBtn($el);
$el.$btngroup.insertBefore($el.$btnShare, $el.$btngroup.childNodes[0]);
$el.dataset.injected = true;
}
// 查找目标元素
findTarget(next) {
if (!this.$root) {
return;
}
// var $el = this.$root.querySelector('.ant-tabs-tabpane.ant-tabs-tabpane-active.eb-home-tabs-tab.eb-home-tabs-tab-viewForm .eb-view-toolbar-form-head>.ant-space.ant-formily-button-group');
var $el = this.$root.querySelector('.ant-tabs-tabpane.ant-tabs-tabpane-active.eb-home-tabs-tab.eb-home-tabs-tab-viewForm');
$el = $el || this.$root.querySelector('.eb-share-form-view');
if (!$el) {
if (typeof next === 'function') {
next($el);
}
return false;
}
$el.$btngroup = $el.querySelector('.eb-view-toolbar-form-head>.ant-space.ant-formily-button-group');
if (!$el.$btngroup) {
if (typeof next === 'function') {
next($el);
}
return false;
}
if ($el.querySelector(this.#btnSelector)) {
console.warn(`[WARN] 已注入 ${ this.name }`);
return false;
}
this.inject($el);
return $el;
}
// 获取当前 tab 信息
getTabInfo(tabId) {
if (!tabId) {
return console.error('[ERR]', `tabId is ${ tabId }`);
}
var info = null;
var tabs = window.sessionStorage.getItem(USER_INFO.get('keyTabs'));
try {
tabs = JSON.parse(tabs);
if (tabs && tabs.length) {
tabs = tabs.filter((item) => item.id === tabId);
if (tabs.length) {
info = tabs[0];
}
}
} catch (ex) {
console.error(ex);
}
return info;
}
// 获取详情页字段信息
getDetailfields($el) {
var fields = {};
// $el is current tab
if (!$el) {
return fields;
}
var $gridItems = $el.querySelectorAll('.ant-formily-grid-layout>.ant-formily-item');
$gridItems.forEach((item) => {
var text = item.innerText.replace(/\n/g, '');
if (text.startsWith('标题:') || text.startsWith('需求标题:')) {
fields.title = text.split(':').pop();
}
if (text.startsWith('所属项目:')) {
fields.project = text.split(':').pop();
}
});
return fields;
}
createBtn($el) {
// $el is current tab
if (!$el) {
return;
}
var $btnCopy = document.createElement('div');
$btnCopy.className = 'ant-space-item';
$btnCopy.innerHTML = `<a class="${ this.#btnClassName }" target="_blank" rel="opener" href="javascript:void(0)" title="复制分享链接">分享</a>`;
$btnCopy.$link = $btnCopy.querySelector(this.#btnSelector);
$btnCopy.$link.addEventListener('click', this.onBtnClick($el));
return $btnCopy;
}
doCopy(event, data) {
doCopy(`${ (data.project ? `【${ data.project }】 ` : '') }${ data.title } \n${ data.url }`).then(() => {
event.target.innerText = '复制成功';
}).finally(() => {
setTimeout(() => {
event.target.innerText = '分享';
}, 3000);
});
}
onBtnClick($el) {
let data = null;
let loading = false;
let canShare = true;
return (event) => {
event.stopPropagation();
if (!event.ctrlKey || event.target.getAttribute('href').includes('javascript:void(0)') || data) {
event.preventDefault();
if (data && data.url) {
return this.doCopy(event, data);
}
// 分享详情页
if ($el.classList.contains('eb-share-form-view')) {
data = this.getDetailfields($el);
/*if (data.title && window.parent.$Y && window.parent.$Y.$app && window.parent.$Y.$app.$search) {
window.parent.$Y.$app.$search.$keyword.value = data.title;
}
*/
if (window.location.hash.startsWith('#/share/form?number=')) {
data.url = location.hash.substr(1);
}
if ((window.location.pathname + window.location.search).startsWith('/share/form?number=')) {
data.url = window.location.pathname + window.location.search;
}
if (data.url) {
data.url = data.url.split('&callback')[0];
}
if (data.url) {
data.url = `https://web.yidayun.com${ data.url }`;
event.target.setAttribute('href', data.url);
// console.log('share.data:', data);
this.doCopy(event, data);
return false;
}
// console.log('share.data:', data);
canShare = false;
event.target.innerText = '无法分享';
event.target.title = '解析异常了';
return false;
}
// 详情页
if (!canShare || loading) {
return false;
}
var tabId = $el.id.split('-panel-').pop();
if (!tabId) {
return console.warn(`找不到 对应的 tabId`, tabId);
}
var tabInfo = this.getTabInfo(tabId);
if (!tabInfo) {
return console.warn(`找不到 对应的 tab`, tabId, tabInfo);
}
data = this.getDetailfields($el);
// 请求数据
event.target.innerText = '获取中...';
this.getShareLink({objectNumber: tabInfo.object, dataId: tabInfo.resId}).then((res) => {
data = { ...data, ...res };
// console.log('data:', data);
if (data.url) {
event.target.setAttribute('href', data.url);
this.doCopy(event, data);
} else {
canShare = false;
event.target.innerText = '无法分享';
event.target.title = '易搭云不支持';
}
}).finally(() => {
loading = false;
});
return false;
}
}
}
watchEvents() {
const findEl = () => {
if (this.timer_finder) {
clearTimeout(this.timer_finder);
this.timer_finder = null;
}
this.timer_finder = setTimeout(() => {
this.findTarget(($el) => {
this.timer_finder = null;
if ($el && $el.dataset.injected !== 'true') {
this.timer_finder = setTimeout(() => {
this.timer_finder = null;
this.findTarget();
}, 600);
}
});
}, 600);
}
// 监听 查找 可以分享的页面
this.$root.addEventListener('click', (event) => {
if (!event.clientX && !event.clientY) {
return false;
}
findEl();
// console.log('body.clicked', event);
});
setTimeout(() => {
findEl();
}, 1200);
console.log(`[OK] 初始化 ${ this.name } 监听事件`);
}
init() {
if (this.inited) {
return console.warn(`[WARN] 已经初始化过了`);
}
this.$root = document.querySelector('#root');
// this.$root = document.body;
if (!this.$root) {
return console.error(`[ERROR] 监控父元素未找到`);
}
this.watchEvents();
this.inited = true;
}
}
class PLAYERS {
name = '参与人选择器';
#key = 'y-app-players';
#separator = ',';
$root = null;
constructor() {
console.log(`[OK] 初始化 参与人选择器 ...`);
this.init();
}
parserArray(str) {
str = (str || '').toString().trim();
str = str.replace(/(,|,|;|\s)+/ig, this.#separator);
str = str.split(this.#separator);
str = str.filter((item, index, array) => {
return item.trim().length && array.indexOf(item) === index;
});
return str;
}
get() {
let data = window.localStorage.getItem(this.#key);
return this.parserArray(data);
}
set(data) {
if (typeof data === 'object') {
data = data.join(this.#separator);
}
if (typeof data === 'string') {
data = data.trim();
window.localStorage.setItem(this.#key, data);
}
}
clear() {
window.localStorage.removeItem(this.#key);
}
// 注入样式
injectStyle() {
const cssRules = () => {
return `
.y-app-players{
&>.ant-space{
width: 100%;
}
.y-app-players__editor{
display: none;
}
.y-app-players__selector{
}
&.y-app-players__editing{
.y-app-players__editor{
display: flex;
}
.y-app-players__selector{
display: none;
}
}
.y-app-players__list,
.y-app-players__actions{
padding-top: 3px;
padding-bottom: 3px;
}
.y-app-players__list{
flex: 1;
}
.y-app-players__item,
.y-app-players__placeholder,
.y-app-players__btn{
display: inline-block;
}
.y-app-players__btn,
.y-app-players__item{
min-width: 30px;
text-align: center;
padding: 2px 6px;
border-radius: 3px;
&:hover{
background: #f5f5f5;
}
&:active{
background: #e5e5e5;
}
}
.y-app-players__item{
margin-right: 5px;
margin-bottom: 4px;
}
.y-app-players__item-selected{
color: #fff;
background: #247fff;
&:hover{
background: #4b96ff;
}
&:active{
background: #146eed;
}
}
.y-app-players__placeholder{
color: #999;
padding: 2px 6px;
}
.y-app-players__btn-clear{
color: red;
}
}
div.ant-tabs-tabpane-active[id^="rc-tabs"][id$="-panel-person"]{
.eb-index-list-bar,
.eb-selector-modal-person-index{
display: none !important;
}
.eb-index-list-container{
margin-top: -20px !important;
}
}
`.trim();
};
const $style = document.createElement('style');
$style.setAttribute('id', 'y-app-style-players');
$style.setAttribute('type', 'text/css');
$style.innerHTML = cssRules();
document.head.appendChild($style);
console.log(`[OK] 初始化 参与人选择器 已注入`);
}
inject($el) {
if (!$el) return false;
var render = (data, defaultValue) => {
var html = [];
var hasSelected = false;
var selectedList = $el.getSelectedList();
// console.log(selectedList, data);
if (Array.isArray(data) && data.length) {
data.forEach((item, index) => {
// hasSelected = selectedList.includes(item);
hasSelected = selectedList.filter((dd) => dd == item).length;
html.push(`<a class="y-app-players__item${ hasSelected ? ' y-app-players__item-selected' : '' }" title="左键单击:选择 右键单击:前移 左键长按:移除" data-index="${ index }">${ item }</a>`);
});
} else {
defaultValue = defaultValue || `<span class="y-app-players__placeholder">搜索后将添加常联系的,多个用空格隔开</span>`;
if (defaultValue) {
html.push(defaultValue);
}
}
return html.join('');
}
// 获取已选择的
$el.getSelectedList = () => {
var ret = $el.$selectedList ? $el.$selectedList.innerText : '';
ret = ret.split('\n');
ret = ret.filter((item) => item.trim());
return ret;
}
// 自动选择
$el.autoSelect = (next) => {
if ($el.timer_search) {
clearTimeout($el.timer_search);
$el.timer_search = null;
}
if ($el.timer_search_render) {
clearTimeout($el.timer_search_render);
$el.timer_search_render = null;
}
$el.timer_search = setTimeout(() => {
$el.$results = $el.$content.querySelectorAll('.eb-selector-modal-person-item');
// 有搜索结果时,保存数据、更新视图
if ($el.$results.length) {
// 只有一个结果时自动选中
if ($el.$results.length === 1) {
$el.$results[0].click();
}
$el.timer_search_render = setTimeout(() => {
if (typeof next === 'function') {
next($el.$results);
}
// 保存数据
this.set($el.data);
// 更新视图
$el.$players.$list.innerHTML = render($el.data).trimHTML();
}, 200);
}
}, 500);
}
// $el.$content = $el.parentNode.parentNode.parentNode.parentNode;
// $el.$body = $el.$content.parentNode;
$el.$content = $el.querySelector('.ant-modal-content');
$el.$body = $el.$content.querySelector('.ant-modal-body');
// 展开已选择
$el.doExpand = () => {
$el.$btnHasSelected = $el.$content.querySelector('a.ant-typography[direction="ltr"]');
if ($el.$btnHasSelected) {
if ($el.classList.contains('eb-selector-modal--open')) {
// console.warn('已经展开');
// 已经展开
return false;
} else {
// 点击展开已选择
$el.$btnHasSelected.click();
}
setTimeout(() => {
$el.$players.$list.innerHTML = render($el.data).trimHTML();
}, 1200);
}
}
// 自动展开已选择
$el.doExpand();
if ($el.$body) {
$el.$body.addEventListener('click', (event) => {
// event.stopPropagation();
return false;
});
$el.$selectedList = $el.$body.querySelector('.eb-selector-modal-drawer-content>.eb-menu-sm');
$el.$selectedList.addEventListener('click', function() {
if ($el.$selectedList.timer_click) {
clearTimeout($el.$selectedList.timer_click);
$el.$selectedList.timer_click = null;
}
$el.$selectedList.timer_click = setTimeout(() => {
$el.$players.$list.innerHTML = render($el.data).trimHTML();
}, 300);
})
}
// console.log('b:', $el.$body, $el.$selectedList);
$el.$tree = $el.querySelector('.eb-selector-modal-tree');
$el.$input = $el.querySelector('input.ant-input[type="text"]');
// 输入
$el.doInput = (value, next) => {
// 赋值给输入框
var lastValue = $el.$input.value;
$el.$input.value = value;
$el.$input.setAttribute('value', value);
var eventInput = new Event('input', { target: $el.$input, bubbles: true });
eventInput.simulated = true;
var tracker = $el.$input._valueTracker;
if (tracker) {
tracker.setValue(lastValue);
}
$el.$input.dispatchEvent(eventInput);
if (typeof next === 'function') {
next();
}
}
// 搜索
$el.doSearch = (next) => {
// 执行搜索
var eventEnter = new KeyboardEvent('keydown', {
code: 'Enter',
key: 'Enter',
charCode: 13,
keyCode: 13,
view: window,
bubbles: true
});
$el.$input.dispatchEvent(eventEnter);
if (typeof next === 'function') {
next();
}
}
// 输入空格组织 垃圾转圈
$el.doInput(' ', () => {
$el.doSearch(() => {
$el.doInput('');
});
});
$el.data = this.get();
$el.$players = document.createElement('div');
$el.$players.className = 'ant-space-item y-app-players';
var html = `
<div class="ant-space y-app-players__tip"><span class="y-app-players__placeholder">左键单击选择,左键长按2秒删除,右键移至最前</span></div>
<div class="ant-space y-app-players__selector">
<div class="ant-space-item y-app-players__list"></div>
<div class="ant-space-item y-app-players__actions">
<a class="y-app-players-btn y-app-players__btn-clear" title="三思 !!! 清空保存的历史 左键长按姓名也可删除">清空</a>
</div>
</div>
`.trim();
$el.$players.innerHTML = html.trimHTML();
$el.$players.$list = $el.$players.querySelector('.y-app-players__list');
$el.$players.$list.innerHTML = render($el.data).trimHTML();
// 双击 复制全部常联系的
$el.$players.$list.addEventListener('dblclick', (event) => {
event.preventDefault();
event.stopPropagation();
if (event.target.tagName === 'A') {
return false;
}
var value = $el.data.join(' ');
doCopy(value).then(() => {
console.log(`常联系的 复制成功`);
console.log(value);
});
});
// 右键 排序
$el.$players.$list.addEventListener('contextmenu', (event) => {
event.preventDefault();
event.stopPropagation();
if (event.target.tagName !== 'A' && event.target.dataset.index === undefined) {
return false;
}
var index = Number(event.target.dataset.index);
if (isNaN(index)) {
// console.warn(`index isNaN`, event.target.dataset.index);
return false;
}
// 移动至第一
var value = $el.data.splice(index, 1);
$el.data.unshift(value);
this.set($el.data);
$el.$players.$list.innerHTML = render($el.data).trimHTML();
});
// 长按2秒 移除
$el.$players.$list.addEventListener('mousedown', (event) => {
event.preventDefault();
event.stopPropagation();
if (event.target.tagName !== 'A' && event.target.dataset.index === undefined) {
return false;
}
if (event.button !== 0) {
return false;
}
var index = Number(event.target.dataset.index);
if (isNaN(index)) {
// console.warn(`index isNaN`, event.target.dataset.index);
return false;
}
var value = $el.data[index];
if (!value) {
return false;
}
if ($el.$players.$list.timer_mousedown) {
clearTimeout($el.$players.$list.timer_mousedown);
$el.$players.$list.timer_mousedown = null;
}
$el.$players.$list.dataset.mousedowned = true;
$el.$players.$list.timer_mousedown = setTimeout(() => {
// 移除
$el.data.splice(index, 1);
this.set($el.data);
$el.$players.$list.innerHTML = render($el.data).trimHTML();
}, 2000);
});
$el.$players.$list.addEventListener('mouseup', (event) => {
if ($el.$players.$list.timer_mousedown) {
clearTimeout($el.$players.$list.timer_mousedown);
$el.$players.$list.timer_mousedown = null;
}
});
// 左键 选择
$el.$players.$list.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
if (event.target.tagName !== 'A' && event.target.dataset.index === undefined) {
return false;
}
if (event.button !== 0) {
return false;
}
var index = Number(event.target.dataset.index);
if (isNaN(index)) {
// console.warn(`index isNaN`, event.target.dataset.index);
return false;
}
var value = $el.data[index];
if (!value) {
return false;
}
$el.doInput(value, $el.doSearch);
// 自动选择
$el.autoSelect(() => {
// 最近点击的插入到最前排
// value = $el.data.splice(index, 1);
// $el.data.unshift(value);
});
});
// 清空 按钮
$el.$players.$clear = $el.$players.querySelector('.y-app-players__btn-clear');
$el.$players.$clear.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
$el.data = [];
// window.localStorage.removeItem('y-app-players');
this.clear();
$el.$players.$list.innerHTML = render($el.data).trimHTML();
});
$el.$tree.insertBefore($el.$players, $el.$tree.childNodes[0]);
// 搜索框 事件
$el.$input.addEventListener('keyup', (event) => {
if (event.key !== 'Enter') {
return false;
}
var value = $el.$input.value.trim();
if (!value) {
return false;
}
var currentValue = value;
value = this.parserArray(value);
value = value.concat($el.data);
value = this.parserArray(value.join(','));
$el.data = value;
$el.$players.$list.innerHTML = render(value).trimHTML();
this.set($el.data);
// 自动选择
$el.autoSelect();
});
// 标记元素已经找到
$el.dataset.injected = true;
return $el;
}
findModal(next) {
// var $el = document.querySelector('div.ant-tabs-tabpane-active[id^="rc-tabs"][id$="-panel-person"]');
var $el = document.querySelector('.ant-modal-wrap:not([style*="none"])>.ant-modal.eb-selector-modal');
if (!$el) {
if (typeof next === 'function') {
next($el);
}
return false;
}
if ($el.querySelector('.y-app-players')) {
console.warn(`[WARN] 已注入 参与人选择器`);
if ($el.doExpand) {
$el.doExpand();
}
return false;
}
this.inject($el);
return $el;
}
watchEvents() {
// 监听 查找 参与人选择 弹窗
this.$root.addEventListener('click', (event) => {
if (this.timer_finder) {
clearTimeout(this.timer_finder);
this.timer_finder = null;
}
if (!event.clientX && !event.clientY) {
return false;
}
this.timer_finder = setTimeout(() => {
this.findModal(($el) => {
this.timer_finder = null;
if ($el && $el.dataset.injected !== 'true') {
this.timer_finder = setTimeout(() => {
this.timer_finder = null;
this.findModal();
}, 600);
}
});
}, 600);
// console.log('body.clicked', event);
});
console.log(`[OK] 初始化 参与人选择器 监听事件`);
}
init() {
if (this.inited) {
return console.warn(`[WARN] 参与人选择器 已经初始化过了`);
}
// this.$root = document.querySelector('#root');
this.$root = document.body;
if (!this.$root) {
return console.error(`[ERROR] 监控父元素未找到`);
}
this.watchEvents();
this.injectStyle();
this.inited = true;
}
}
class BOOKMARKS {
name = '收藏夹';
#key = 'y-app-bookmarks';
#DB = {};
constructor() {
console.log(`[OK] 初始化 收藏夹 ...`);
this.#load();
}
#check = (data) => {
if (!data || typeof data !== 'object') {
console.warn(`[WARN] 参数错误, 类型错误, 只接受简单对象`, data)
return false;
}
if (!data.object) {
console.warn(`[WARN] 参数错误, 缺少object`, data.object)
return false;
}
if (!data.resId) {
console.warn(`[WARN] 参数错误, 缺少resId`, data.resId)
return false;
}
return true;
}
#load = () => {
let DB = window.localStorage.getItem(this.#key);
if (!DB) {
return;
}
try {
DB = JSON.parse(DB);
if (typeof DB === 'object') {
for (var object in DB) {
if (Array.isArray(DB[object]) && DB[object].length) {
DB[object].forEach((item, index) => {
((_item, _index) => {
if (_item.resId) {
DB[object][_item.resId] = DB[object][_index];
}
})(item, index);
});
}
}
this.#DB = DB;
}
} catch (ex) {
console.error(`[ERROR] 加载&解析数据出错`, ex);
}
}
#save = () => {
window.localStorage.setItem(this.#key, JSON.stringify(this.#DB));
}
get length() {
const DB = this.#DB;
let count = 0;
for (var object in DB) {
count = count + parseInt(DB[object].length);
}
return count;
}
get get() {
return (viewObject, index) => {
if (typeof viewObject === 'string' && viewObject) {
const viewData = this.#DB[viewObject];
if (viewData && index !== undefined) {
return viewData[index];
}
return viewData;
}
return this.#DB;
}
}
get add() {
return (data, callback) => {
if (!this.#check(data)) {
return false;
}
const DB = this.#DB;
if (!Array.isArray(DB[data.object])) {
DB[data.object] = [];
}
if (this.has(data)) {
this.remove(data, callback);
} else {
delete data.bookmark;
DB[data.object].unshift(data);
DB[data.object][data.resId] = DB[data.object][0];
if (typeof callback === 'function') {
callback({ type: 'add', data, index: 0 });
}
this.#save();
}
}
}
get has() {
return (data) => {
if (!this.#check(data)) {
return false;
}
const DB = this.#DB;
if (Array.isArray(DB[data.object]) && DB[data.object][data.resId]) {
return true;
}
return false;
}
}
get remove() {
return (data, callback) => {
if (!this.#check(data)) {
return false;
}
if (!this.has(data)) {
return false;
}
const DB = this.#DB;
DB[data.object].some((item, index) => {
if (data.resId === item.resId) {
DB[data.object].splice(index, 1);
delete DB[data.object][data.resId];
if (typeof callback === 'function') {
callback({ type: 'remove', data: item, index });
}
this.#save();
return true;
}
});
}
}
}
class Y {
#KEY = {
state: 'y-app-state'
};
get name() {
return '易用云';
}
get desc() {
return '让易搭云更易用';
}
get author() {
return 'Jack.Chan';
}
get path() {
return window.location.pathname + window.location.search;
}
get query() {
return new URLSearchParams(window.location.search.substr(1));
}
#STATE = {
pagesize: 5,
worktime: '9:00',
involved: false,
types: {}
};
get state() {
return this.#STATE;
}
set state(state) {
return this.#STATE = { ...state };
}
get saveState() {
return () => {
window.localStorage.setItem(this.#KEY.state, JSON.stringify(this.state));
}
}
get loadState() {
return () => {
let state = window.localStorage.getItem(this.#KEY.state);
if (state) {
try {
state = JSON.parse(state);
if (typeof state !== 'object') {
state = {}
}
} catch (ex) {
console.warn(ex);
}
} else {
state = {};
}
this.state = { ...this.state, ...state };
return state;
}
}
get setState() {
return (state, callback) => {
if (typeof state === 'object') {
Object.keys(state).forEach((key) => {
this.state.types[key] = state[key];
});
}
if (typeof callback === 'function') {
callback(state);
}
}
}
get USER() {
return USER_INFO.get();
}
$app = null;
$fetch = $fetch;
// 收藏夹
BOOKMARKS = null;
// 参与人选择器
PLAYERS = null;
// 分享按钮
BTN_SHARE = null;
// 分页
#PAGE_SIZES = [
{ value: 5, selected: true },
{ value: 10 },
{ value: 20 },
{ value: 30 },
{ value: 50 },
{ value: 100 },
{ value: 200 }
];
// 视图: 对象名、视图编号、查询参数、模板渲染器、数据
#VIEWS = [
{
name: '需求',
object: 'eg68i5r43aq',
viewNumber: 'baz6tzfuvkf',
// 审批流
context: { flowFlag: true },
checked: true,
records: [],
render: (view, records, options) => {
options = { from: 'search', ...options };
if (!view || !records || !records.length) {
return '';
}
return `
<table class="y-app-table">
<caption class="y-app-table__caption">${ view.name }</caption>
<colgroup>
<col style="width: 40px;" />
<col style="width: 40px;" />
<col style="width: auto;min-width: 400px;max-width: 900px;" />
<col style="width: 100px;" />
<col style="width: 160px;" />
<col style="width: 140px;" />
<col style="width: 140px;" />
<col style="width: auto;" />
</colgroup>
${ records.map((item, index) => {
if (!item.bookmark && !item.object) {
item.bookmark = {
object: view.object,
viewNumber: view.viewNumber,
project: item[view.object +'Header_f_zc8nx8Name'],
name: item[view.object +'Header_f_h8bqqj'],
resId: item[view.object +'Header_id'],
creator: item[view.object +'Header_creatorName'],
players: renderPlayers(item[view.object +'Header_f_b7vp4dName']),
createTime: formatDate(item[view.object +'Header_createTime']),
updateTime: formatDate(item[view.object +'Header_updateTime']),
devStatus: item[view.object +'Header_f_giqar8Name'],
priority: item[view.object +'Header_f_d37i79Name'],
};
item.bookmark = trimObject(item.bookmark);
if (view.context) {
item.bookmark.context = view.context;
}
item.bookmark.title = (item.bookmark.project ? `【${ item.bookmark.project }】` : '') + item.bookmark.name;
item.bookmark.url = renderLinkHref(item.bookmark);
for (var prop in item.bookmark) {
item[prop] = item.bookmark[prop];
}
};
item.starred = this.BOOKMARKS.has(item);
return `
<tr>
<td>
<a class="y-app-btn y-app-btn-icon y-app-action" data-from="${ options.from }" data-action="prevent.star" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }" title="${ item.starred ? '取消收藏' : '收藏' }">${ ICONS[item.starred ? 'starred' : 'star'] }</a>
</td>
<td>
<!-- <a target="_blank" href="${ item.url }" class="y-app-btn y-app-btn-icon y-app-action" data-action="prevent.copy" data-project="${ item.project }" data-name="${ item.name }" data-title="${ item.title }" data-from="${ options.from }" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }" title="复制链接">${ ICONS.copy }</a> -->
<a target="_blank" href="${ item.url }" class="y-app-btn y-app-btn-icon y-app-action" data-action="prevent.copy" data-project="${ item.project }" data-name="${ item.name }" data-title="${ item.title }" title="复制链接">${ ICONS.copy }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-link y-app-action" data-from="${ options.from }" data-action="prevent.openDrawer" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }">${ item.title }</a>
</td>
<td><span title="创建人:${ item.creator } 参与人:${ item.players }">${ item.creator }</span></td>
<td><span title="创建时间:${ item.createTime } 更新时间:${ item.updateTime }">${ item.updateTime }</span></td>
<td><span title="需求状态">${ item.devStatus }</span></td>
<td><span title="优先级">${ item.priority }</span></td>
<td> </td>
</tr>
`.trim();
}).join('') }
</table>
`.trim();
},
params(query) {
const params = {
"params": {
"object": "eg68i5r43aq",
"viewType": "viewList",
"size": query.size || 50,
"page": query.page || 1,
"search": {
"type": "every",
"fields": [
"wdl0ln185al",
"moch002vze6",
"fnse19vmu5t",
"s0nvbqzlu58",
"g8qcmno34ix",
"7h90amrzdhu",
"kntb9eao69o",
"k3979lviw8g",
"ejnq8nbp1ks"
],
"value": query.keyword || ''
},
"fields": [
"wdl0ln185al",
"moch002vze6",
"fnse19vmu5t",
"s0nvbqzlu58",
"5fftccbn4vn",
"g8qcmno34ix",
"7h90amrzdhu",
"kntb9eao69o",
"k3979lviw8g",
"4i4hooso8xw",
"9i7p66yp7hj",
"prpqzpxifvy",
"ejnq8nbp1ks"
],
"filter": {
"rel": "and",
"rules": []
},
"summary": [],
"sort": [
{
"field": "eg68i5r43aqHeaderCreateTime",
"number": "m81dt0b9u16",
"order": "descend",
"orderType": "DESC"
}
],
"mobileOptions": null,
"context": {}
},
"context": {}
};
// 与我有关的
if (query.involved) {
params.params.filter = {
"rel": "and",
"rules": [
{
"number": "w0j1vpyrui4",
"rel": "or",
"rules": [
{
"number": "ybugz2uwya6",
"lType": "field",
"lValue": [
"eg68i5r43aqHeader",
"fnse19vmu5t"
],
"op": "in",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
},
{
"number": "ztg1twd9r02",
"lType": "field",
"lValue": [
"eg68i5r43aqHeader",
"7ft4nvinhbs"
],
"op": "contains",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
},
{
"number": "am4fq8ostla",
"lType": "field",
"lValue": [
"eg68i5r43aqHeader",
"eg68i5r43aqHeaderCreator"
],
"op": "in",
"rType": "constant",
"rValue": [
// "1594888324594606163"
USER_INFO.get('id')
]
}
]
},
{
"number": "lva1dayvzg7",
"rel": "and",
"rules": [
{
"number": "x29396d8gal",
"lType": "field",
"lValue": [
"eg68i5r43aqHeader",
"k3979lviw8g"
],
"op": "in",
"rType": "constant",
"rValue": [
"rs3rh8da323",
"e0vnqk6rvy5",
"5bn9ayf0sgk",
"zbs0i8e1hdz",
"56g8zm7gkdo",
"o49jkiqkmnv",
"w7t6sisqu31",
"3gb5v85bprv",
"9sme879pbvo"
]
}
]
}
]
};
}
return params;
},
},
{
name: '缺陷',
object: 'jkubb5t4pj7',
viewNumber: 'prgp74dayd5',
context: { flowFlag: true },
checked: true,
render: (view, records, options) => {
options = { from: 'search', ...options };
if (!view || !records || !records.length) {
return '';
}
return `
<table class="y-app-table">
<caption class="y-app-table__caption">${ this.#VIEWS[view.object].name }</caption>
<colgroup>
<col style="width: 40px;" />
<col style="width: 40px;" />
<col style="width: auto;min-width: 400px;max-width: 900px;" />
<col style="width: 100px;" />
<col style="width: 160px;" />
<col style="width: 140px;" />
<col style="width: 140px;" />
<col style="width: auto;" />
</colgroup>
${ records.map((item, index) => {
if (!item.bookmark && !item.object) {
item.bookmark = {
object: view.object,
viewNumber: view.viewNumber,
project: item[view.object +'Header_f_tavfi9Name'],
name: item[view.object +'Header_f_k8qz5t'],
resId: item[view.object +'Header_id'],
creator: item[view.object +'Header_creatorName'],
players: renderPlayers(item[view.object +'Header_f_tx98jjName']),
createTime: formatDate(item[view.object +'Header_createTime']),
updateTime: formatDate(item[view.object +'Header_updateTime']),
devStatus: item[view.object +'Header_f_etietbName'],
priority: item[view.object +'Header_f_bw7iwcName'],
};
item.bookmark = trimObject(item.bookmark);
if (view.context) {
item.bookmark.context = view.context;
}
item.bookmark.title = (item.bookmark.project ? `【${ item.bookmark.project }】` : '') + item.bookmark.name;
item.bookmark.url = renderLinkHref(item.bookmark);
for (var prop in item.bookmark) {
item[prop] = item.bookmark[prop];
}
};
item.starred = this.BOOKMARKS.has(item);
return `
<tr>
<td>
<a class="y-app-btn y-app-btn-icon y-app-action" data-from="${ options.from }" data-action="prevent.star" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }" title="${ item.starred ? '取消收藏' : '收藏' }">${ ICONS[item.starred ? 'starred' : 'star'] }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-btn y-app-btn-icon y-app-action" data-action="prevent.copy" data-project="${ item.project }" data-name="${ item.name }" data-title="${ item.title }" title="复制链接">${ ICONS.copy }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-link y-app-action" data-from="${ options.from }" data-action="prevent.openDrawer" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }">${ item.title }</a>
</td>
<td><span title="创建人:${ item.creator } 参与人:${ item.players }">${ item.creator }</span></td>
<td><span title="创建时间:${ item.createTime } 更新时间:${ item.updateTime }">${ item.updateTime }</span></td>
<td><span title="缺陷状态">${ item.devStatus }</span></td>
<td><span title="优先级">${ item.priority }</span></td>
<td> </td>
</tr>
`.trim();
}).join('') }
</table>
`.trim();
},
params(query) {
const params = {
"params": {
"object": "jkubb5t4pj7",
"viewType": "viewList",
"size": query.size || 50,
"page": query.page || 1,
"search": {
"type": "every",
"fields": [
"rq8rbzr8mtx",
"zii2oihcraf",
"xjlih2lj64m",
"6s6pnhzilf3",
"n3ej7s7ksuq",
"jkubb5t4pj7HeaderCreator",
"anefrl25i1a"
],
"value": query.keyword || ''
},
"fields": [
"rq8rbzr8mtx",
"zii2oihcraf",
"xjlih2lj64m",
"6s6pnhzilf3",
"n3ej7s7ksuq",
"jkubb5t4pj7HeaderCreator",
"jkubb5t4pj7HeaderCreateTime",
"anefrl25i1a"
],
"filter": {
"rel": "and",
"rules": []
},
"summary": [],
"sort": [
{
"field": "jkubb5t4pj7HeaderCreateTime",
"number": "mj2e3fjgeyu",
"order": "descend",
"orderType": "DESC"
}
],
"mobileOptions": null,
"context": {}
},
"context": {}
};
// 与我有关的 缺陷
if (query.involved) {
params.params.filter = {
"rel": "and",
"rules": [
{
"number": "gyzggqlk5u1",
"rel": "or",
"rules": [
{
"number": "gjvcsiln78q",
"lType": "field",
"lValue": [
"jkubb5t4pj7Header",
"jkubb5t4pj7HeaderCreator"
],
"op": "in",
"rType": "constant",
"rValue": [
// "1594888324594606163"
USER_INFO.get('id')
]
},
{
"number": "maq433j9400",
"lType": "field",
"lValue": [
"jkubb5t4pj7Header",
"anefrl25i1a"
],
"op": "contains",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
},
{
"number": "mwzmb66egcc",
"lType": "field",
"lValue": [
"jkubb5t4pj7Header",
"xjlih2lj64m"
],
"op": "in",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
}
]
},
{
"number": "35a5b8esisp",
"rel": "and",
"rules": [
{
"number": "rc9md2gbyb5",
"lType": "field",
"lValue": [
"jkubb5t4pj7Header",
"zii2oihcraf"
],
"op": "in",
"rType": "constant",
"rValue": [
"hovq5gukui3",
"hyreb65tq3l",
"99gbf8mrn0j",
"yha99te9o7j",
"lvwjsakiu6v",
"lahf9eis5sz"
]
}
]
}
]
};
}
return params;
},
},
{
name: '研发任务',
object: 'uzze6x8nxxz',
viewNumber: 'zgezk76v77m',
// context: { flowFlag: true },
checked: true,
render: (view, records, options) => {
options = { from: 'search', ...options };
if (!view || !records || !records.length) {
return '';
}
return `
<table class="y-app-table">
<caption class="y-app-table__caption">${ view.name }</caption>
<colgroup>
<col style="width: 40px;" />
<col style="width: 40px;" />
<col style="width: auto;min-width: 400px;max-width: 900px;" />
<col style="width: 100px;" />
<col style="width: 160px;" />
<col style="width: 140px;" />
<col style="width: 140px;" />
<col style="width: auto;" />
</colgroup>
${ records.map((item, index) => {
if (!item.bookmark && !item.object) {
item.bookmark = {
object: view.object,
viewNumber: view.viewNumber,
project: item[view.object +'Header_f_x6k7veName'],
name: item[view.object +'Header_f_qg8xig'],
resId: item[view.object +'Header_id'],
creator: item[view.object +'Header_creatorName'],
players: renderPlayers(item[view.object +'Header_f_uqukrwName']),
createTime: formatDate(item[view.object +'Header_createTime']),
updateTime: formatDate(item[view.object +'Header_updateTime']),
devStatus: item[view.object +'Header_f_r876g4Name'],
priority: item[view.object +'Header_f_w2wqevName'],
};
item.bookmark = trimObject(item.bookmark);
if (view.context) {
item.bookmark.context = view.context;
}
item.bookmark.title = (item.bookmark.project ? `【${ item.bookmark.project }】` : '') + item.bookmark.name;
item.bookmark.url = renderLinkHref(item.bookmark);
for (var prop in item.bookmark) {
item[prop] = item.bookmark[prop];
}
};
item.starred = this.BOOKMARKS.has(item);
return `
<tr>
<td>
<a class="y-app-btn y-app-btn-icon y-app-action" data-from="${ options.from }" data-action="prevent.star" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }" title="${ item.starred ? '取消收藏' : '收藏' }">${ ICONS[item.starred ? 'starred' : 'star'] }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-btn y-app-btn-icon y-app-action" data-action="prevent.copy" data-project="${ item.project }" data-name="${ item.name }" data-title="${ item.title }" title="复制链接">${ ICONS.copy }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-link y-app-action" data-from="${ options.from }" data-action="prevent.openDrawer" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }">${ item.title }</a>
</td>
<td><span title="创建人:${ item.creator } 参与人:${ item.players }">${ item.creator }</span></td>
<td><span title="创建时间:${ item.createTime } 更新时间:${ item.updateTime }">${ item.updateTime }</span></td>
<td><span title="任务状态">${ item.devStatus }</span></td>
<td><span title="优先级">${ item.priority }</span></td>
<td> </td>
</tr>
`.trim();
}).join('') }
</table>
`.trim();
},
params(query) {
const params = {
"params": {
"object": "uzze6x8nxxz",
"viewType": "viewList",
"size": query.size || 50,
"page": query.page || 1,
"search": {
"type": "every",
"fields": [
"z8fmgy96ijx",
"885kt2czitt",
"l6veducif7d",
"pkegrllxfey",
"j0v1yxq1l5y",
"odp4x7373wz",
"0wqxezfjh9u",
"uzze6x8nxxzHeaderCreator"
],
"value": query.keyword || ''
},
"fields": [
"z8fmgy96ijx",
"885kt2czitt",
"l6veducif7d",
"pkegrllxfey",
"j0v1yxq1l5y",
"odp4x7373wz",
"q93c6g227n6",
"0wqxezfjh9u",
"uzze6x8nxxzHeaderCreator",
"uzze6x8nxxzHeaderCreateTime"
],
"filter": {
"rel": "and",
"rules": []
},
"summary": [],
"sort": [
{
"field": "uzze6x8nxxzHeaderCreateTime",
"number": "jl5fu6lbna2",
"order": "descend",
"orderType": "DESC"
}
],
"mobileOptions": null,
"context": {}
},
"context": {}
};
// 与我有关的 研发任务
if (query.involved) {
params.params.filter = {
"rel": "and",
"rules": [
{
"number": "x17ylktt0za",
"rel": "or",
"rules": [
{
"number": "bqy2lv1swwr",
"lType": "field",
"lValue": [
"uzze6x8nxxzHeader",
"l6veducif7d"
],
"op": "in",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
},
{
"number": "w63dar6u7rc",
"lType": "field",
"lValue": [
"uzze6x8nxxzHeader",
"ydc3y16bjzw"
],
"op": "contains",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
},
{
"number": "92o07r5w4ki",
"lType": "field",
"lValue": [
"uzze6x8nxxzHeader",
"uzze6x8nxxzHeaderCreator"
],
"op": "in",
"rType": "constant",
"rValue": [
// "1594888324594606163"
USER_INFO.get('id')
]
}
]
},
{
"number": "nzu45djw3nr",
"rel": "and",
"rules": [
{
"number": "lcuyzo8ytvm",
"lType": "field",
"lValue": [
"uzze6x8nxxzHeader",
"885kt2czitt"
],
"op": "in",
"rType": "constant",
"rValue": [
"012j1zkzcb1",
"kjhop9uygpk",
"upuidb76ls8",
"i4hz50jso38"
]
}
]
}
]
};
}
return params;
},
},
{
name: '部门任务',
object: 'heemdyu3ggi',
viewNumber: 'jjh9bh45jea',
// context: { flowFlag: true },
checked: false,
render: (view, records, options) => {
options = { from: 'search', ...options };
if (!view || !records || !records.length) {
return '';
}
return `
<table class="y-app-table">
<caption class="y-app-table__caption">${ view.name }</caption>
<colgroup>
<col style="width: 40px;" />
<col style="width: 40px;" />
<col style="width: auto;min-width: 400px;max-width: 900px;" />
<col style="width: 100px;" />
<col style="width: 160px;" />
<col style="width: 140px;" />
<col style="width: 140px;" />
<col style="width: auto;" />
</colgroup>
${ records.map((item, index) => {
if (!item.bookmark && !item.object) {
item.bookmark = {
object: view.object,
viewNumber: view.viewNumber,
project: '',
name: item[view.object +'Header_f_bq54vn'],
resId: item[view.object +'Header_id'],
creator: item[view.object +'Header_creatorName'],
players: renderPlayers(item[view.object +'Header_f_axzi2cName']),
createTime: formatDate(item[view.object +'Header_createTime']),
updateTime: formatDate(item[view.object +'Header_updateTime']),
devStatus: item[view.object +'Header_f_bjzbdaName'],
type: item[view.object +'Header_f_retjgaName'],
priority: item[view.object +'Header_f_ft8ivjName'],
};
item.bookmark = trimObject(item.bookmark);
if (view.context) {
item.bookmark.context = view.context;
}
item.bookmark.title = (item.bookmark.project ? `【${ item.bookmark.project }】` : '') + item.bookmark.name;
item.bookmark.url = renderLinkHref(item.bookmark);
for (var prop in item.bookmark) {
item[prop] = item.bookmark[prop];
}
};
item.starred = this.BOOKMARKS.has(item);
return `
<tr>
<td>
<a class="y-app-btn y-app-btn-icon y-app-action" data-from="${ options.from }" data-action="prevent.star" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }" title="${ item.starred ? '取消收藏' : '收藏' }">${ ICONS[item.starred ? 'starred' : 'star'] }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-btn y-app-btn-icon y-app-action" data-action="prevent.copy" data-project="${ item.project }" data-name="${ item.name }" data-title="${ item.title }" title="复制链接">${ ICONS.copy }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-link y-app-action" data-from="${ options.from }" data-action="prevent.openDrawer" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }">${ item.name }</a>
</td>
<td><span title="创建人:${ item.creator } 参与人:${ item.players }">${ item.creator }</span></td>
<td><span title="创建时间:${ item.createTime } 更新时间:${ item.updateTime }">${ item.updateTime }</span></td>
<td><span title="任务状态">${ item.devStatus }</span></td>
<td><span title="优先级">${ item.priority }</span></td>
<td> </td>
</tr>
`.trim();
}).join('') }
</table>
`.trim();
},
params(query) {
const params = {
"params": {
"object": "heemdyu3ggi",
"viewType": "viewList",
"size": query.size || 50,
"page": query.page || 1,
"search": {
"type": "every",
"fields": [
"dmp03a0i2r4",
"epi1wxmdd04",
"yypr9li882x",
"uk5x29bns2z",
"trde6pekcxt",
"n6abi2jey2b",
"heemdyu3ggiHeaderCreator",
"wd2ipbnksc1"
],
"value": query.keyword || ''
},
"fields": [
"dmp03a0i2r4",
"epi1wxmdd04",
"yypr9li882x",
"uk5x29bns2z",
"trde6pekcxt",
"4sjyfqn7n11",
"n6abi2jey2b",
"heemdyu3ggiHeaderCreator",
"heemdyu3ggiHeaderCreateTime",
"wd2ipbnksc1"
],
"filter": {
"rel": "and",
"rules": []
},
"summary": [],
"sort": [],
"mobileOptions": null,
"context": {}
},
"context": {}
};
// 与我有关的 部门任务
if (query.involved) {
params.params.filter = {
"rel": "and",
"rules": [
{
"number": "vr6sdxlcgud",
"rel": "or",
"rules": [
{
"number": "bb3s2x055g0",
"lType": "field",
"lValue": [
"heemdyu3ggiHeader",
"yypr9li882x"
],
"op": "in",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
},
{
"number": "twjnqnq5m1q",
"lType": "field",
"lValue": [
"heemdyu3ggiHeader",
"heemdyu3ggiHeaderCreator"
],
"op": "in",
"rType": "constant",
"rValue": [
// "1594888324594606163"
USER_INFO.get('id')
]
},
{
"number": "s442b89do2x",
"lType": "field",
"lValue": [
"heemdyu3ggiHeader",
"zqybtgx4h1f"
],
"op": "contains",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
}
]
},
{
"number": "f7e1v2oja5i",
"rel": "and",
"rules": [
{
"number": "pc9y1rs4thh",
"lType": "field",
"lValue": [
"heemdyu3ggiHeader",
"epi1wxmdd04"
],
"op": "in",
"rType": "constant",
"rValue": [
"fu91fdhi7rp",
"zjrwyaws59s",
"5lnbqpwb8gc",
"z1dkt4efztx"
]
}
]
}
]
};
}
return params;
},
},
{
name: '工时',
object: 'wttvmb5t6aj',
viewNumber: 'dyvk29qcvan',
// context: { flowFlag: true },
checked: false,
render: (view, records, options) => {
options = { from: 'search', ...options };
if (!view || !records || !records.length) {
return '';
}
return `
<table class="y-app-table">
<caption class="y-app-table__caption">${ view.name }</caption>
<colgroup>
<col style="width: 40px;" />
<col style="width: 40px;" />
<col style="width: auto;min-width: 400px;max-width: 900px;" />
<col style="width: 100px;" />
<col style="width: 160px;" />
<col style="width: 140px;" />
<col style="width: 140px;" />
<col style="width: auto;" />
</colgroup>
${ records.map((item, index) => {
if (!item.bookmark && !item.object) {
item.bookmark = {
object: view.object,
viewNumber: view.viewNumber,
project: item[view.object +'Header_f_umxhcwName'],
name: item[view.object +'Header_f_j5uc3d'],
resId: item[view.object +'Header_id'],
creator: item[view.object +'Header_creatorName'],
createTime: formatDate(item[view.object +'Header_createTime']),
updateTime: formatDate(item[view.object +'Header_updateTime']),
// 工作类别
type: item[view.object +'Header_f_yk7nu6Name'],
// 任务类别
taskType: item[view.object +'Header_f_vxrv3uName'],
// 工作时长
duration: item[view.object +'Header_f_nssifb'],
};
item.bookmark = trimObject(item.bookmark);
if (view.context) {
item.bookmark.context = view.context;
}
item.bookmark.title = (item.bookmark.project ? `【${ item.bookmark.project }】` : '') + item.bookmark.name;
item.bookmark.url = renderLinkHref(item.bookmark);
for (var prop in item.bookmark) {
item[prop] = item.bookmark[prop];
}
};
item.starred = this.BOOKMARKS.has(item);
return `
<tr>
<td>
<a class="y-app-btn y-app-btn-icon y-app-action" data-from="${ options.from }" data-action="prevent.star" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }" title="${ item.starred ? '取消收藏' : '收藏' }">${ ICONS[item.starred ? 'starred' : 'star'] }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-btn y-app-btn-icon y-app-action" data-action="prevent.copy" data-project="${ item.project }" data-name="${ item.name }" data-title="${ item.title }" title="复制链接">${ ICONS.copy }</a>
</td>
<td>
<a target="_blank" href="${ item.url }" class="y-app-link y-app-action" data-from="${ options.from }" data-action="prevent.openDrawer" data-object="${ view.object }" data-index="${ index }" data-id="${ item.resId }">${ item.title }</a>
</td>
<td><span title="登记人">${ item.creator }</span></td>
<td><span title="登记时间:${ item.createTime } 更新时间:${ item.updateTime }">${ item.createTime }</span></td>
<td><span title="工作时长">${ item.duration }</span></td>
<td><span title="工作类别">${ item.type }</span></td>
<!-- <td><span title="任务类别">${ item.taskType }</span></td> -->
<td> </td>
</tr>
`.trim();
}).join('') }
</table>
`.trim();
},
params(query) {
const params = {
"params": {
"object": "wttvmb5t6aj",
"viewType": "viewList",
"size": query.size || 50,
"page": query.page || 1,
"search": {
"type": "every",
"fields": [
"dpz01mdgyt0",
"3t7v5gnb2ex",
"l377kywr283",
"hivk18of39l",
"77aqr706vif",
"krmlj1gyytz",
"9sgsp65lb5l"
],
"value": query.keyword || ''
},
"fields": [
"s3yq11rqfg8",
"dpz01mdgyt0",
"3t7v5gnb2ex",
"l377kywr283",
"p7gyupeuiu4",
"hivk18of39l",
"77aqr706vif",
"krmlj1gyytz",
"9sgsp65lb5l",
"8rj5nj8ixbo",
"egwokhoyckf"
],
"filter": {
"rel": "and",
"rules": []
},
"summary": [],
"sort": [
{
"field": "s3yq11rqfg8",
"number": "umcedfwmj0k",
"order": "descend",
"orderType": "DESC"
}
],
"mobileOptions": null,
"context": {}
},
"context": {}
};
// 与我有关的 我登记
if (query.involved) {
params.params.filter = {
"rel": "and",
"rules": [
{
"number": "flqvdo4ufwo",
"rel": "and",
"rules": [
{
"number": "tozq0jfutr8",
"lType": "field",
"lValue": [
"wttvmb5t6ajHeader",
"dpz01mdgyt0"
],
"op": "in",
"rType": "constant",
"rValue": [
{
"value": USER_INFO.get('id'),
"number": USER_INFO.get('number'),
"label": USER_INFO.get('name'),
"objectNumber": "sysUser"
}
]
}
]
}
]
};
}
return params;
},
}
];
// 工时视图
#VIEW_MANHOUR = {
object: 'wttvmb5t6aj',
name: '工时',
params(query) {
return {
"params": {
"object": "wttvmb5t6aj",
"viewType": "viewList",
"size": 50,
"page": 1,
"search": {
"type": "every",
"fields": [
"dpz01mdgyt0"
],
"value": query.userName || ''
},
"fields": [
"wttvmb5t6ajNumber",
"s3yq11rqfg8",
],
"filter": {
"rel": "and",
"rules": []
},
"sort": [
{
"field": "s3yq11rqfg8",
"number": "umcedfwmj0k",
"order": "descend",
"orderType": "DESC"
}
],
}
};
},
render(data) {
data = {today: 0, yestoday: 0, now: '', duration: '', ...data };
data.now = (new Date()).toLocaleString('zh-CN', { hour12: false });
return `
<li title="今日登记工时">今日:${ data.today }小时 <span class="y-app-manhour__countdown" title="刷新倒计时">${ data.countdown }s</span></li>
<li title="昨日登记工时">昨日:${ data.yestoday }小时</li>
<li title="当前上班时长">${ data.duration }</li>
<li title="当前时间">时间:${ data.now }</li>
`.trim();
}
};
// 成员工时统计视图
#VIEW_MANHOUR_MEMBER = {
Users: null,
async getUsers() {
const parse = (data) => {
var users = [];
users['deptId_272'] = '上海'; // 上海研发一部
users['deptId_273'] = '上海'; // 上海研发二部
users['deptId_274'] = '上海'; // 上海前端开发部
users['deptId_276'] = '上海'; // 上海产品组
users['deptId_277'] = '上海'; // 上海UED组
users['deptId_306'] = '苏州'; // 苏州研发中心
users['deptId_440'] = '苏州'; // 苏州后端组
users['deptId_441'] = '苏州'; // 苏州前端组
users['deptId_307'] = '苏州'; // 苏州产品组
users['deptId_287'] = '杭州'; // 杭州研发中心
users['deptId_180'] = '杭州'; // 杭州前端组
users['deptId_181'] = '杭州'; // 杭州后端一组
users['deptId_289'] = '杭州'; // 杭州后端二组
users['deptId_179'] = '杭州'; // 杭州产品一组
var locale = null;
var user = null;
data.forEach((item) => {
if (item.sysUserHeader_userStateNumber !== 'sys_user_state_deactivate') {
locale = users['deptId_'+ item.sysUserHeader_mainDeptNumber];
if (locale) {
user = {
id: item.id,
name: item.sysUserHeader_name,
number: item.sysUserHeader_number,
deptName: item.sysUserHeader_mainDeptName,
deptNumber: item.sysUserHeader_mainDeptNumber,
locale
};
users.push(user);
// group of current user
users['user_'+ user.name] = users['user_'+ user.name] || [];
users['user_'+ user.name]['user_'+ user.name]= user;
users['user_'+ user.name].push(user);
// group of mainDeptName
users[locale +'_'+ user.deptName] = users[locale +'_'+ user.deptName] || [];
users[locale +'_'+ user.deptName]['user_'+ user.name]= user;
users[locale +'_'+ user.deptName].push(user);
// group of locale
users[locale] = users[locale] || [];
users[locale]['user_'+ user.name]= user;
users[locale].push(user);
}
}
});
return users;
};
if (this.Users && this.Users.length) {
return Promise.resolve(this.Users);
}
const params = {
"params": {
"object": "sysUser",
"fields": [
"sysUserHeaderName",
"sysUserHeaderDept",
"sysUserHeaderPhone",
"sysUserHeaderEmail",
"sysUserHeaderAvatar",
"sysUserHeaderSurnameFirstChar",
"sysUserHeaderId",
"sysUserHeaderNumber"
],
"page": 1,
"size": 2000,
"sort": [
{
"field": "sysUserHeaderSurnameFirstChar",
"order": "ascend"
}
],
"context": {
"originId": "1728960441309794304",
"originObject": "eg68i5r43aq",
"originEntryId": null,
"originEntryField": null
}
},
"context": {
"originId": "1728960441309794304",
"originObject": "eg68i5r43aq",
"originEntryId": null,
"originEntryField": null
}
};
return await $fetch('https://api.yidayun.com/runtime/getList', params).then((res)=>{
// console.log(res.data);
this.Users = parse(res.data.records);
return this.Users;
}).catch((ex) => {
console.error(`[ERROR]`, ex)
return ex;
});
},
async getManhours(groupby, monthRange) {
await this.getUsers();
if (groupby === 'me') {
groupby = 'user_'+ USER_INFO.get('name');
}
if (groupby === 'all') {
groupby = '';
}
const users = groupby ? this.Users[groupby] : this.Users;
if (!users || !users.length) {
console.error(`[ERROR]`, data)
return;
}
monthRange = monthRange || [ new Date('2023/12/1 00:00:00').getTime(), new Date('2023/12/30 00:00:00').getTime() ];
const params = {
"params": {
"chartType": "cross_pivot",
"chartSchema": {
"dataSource": "wttvmb5t6aj",
"dataSourceType": "form",
"xDimension": [
{
"name": "登记人",
// "number": "x1c52gfh2hi",
"number": "name",
"fieldType": "fieldString",
"modelNumber": "wttvmb5t6ajHeader",
"required": true,
"options": {
"listFlag": false
},
"field": [
"wttvmb5t6ajHeader",
"dpz01mdgyt0Name"
]
}
],
"yDimension": [
{
"name": "工作日期",
// "number": "yubsrcxz2d1",
"number": "date",
"fieldType": "fieldDatetime",
"modelNumber": "wttvmb5t6ajHeader",
"required": true,
"options": {
"listFlag": true
},
"field": [
"wttvmb5t6ajHeader",
"s3yq11rqfg8"
],
"group": "date-day"
}
],
"target": [
{
"name": "工时",
// "number": "ts0ltoz3dkr",
"number": "duration",
"fieldType": "fieldInt",
"modelNumber": "wttvmb5t6ajHeader",
"required": false,
"field": [
"wttvmb5t6ajHeader",
"p7gyupeuiu4"
],
"summaryType": "sum",
"caculate": "noneCaculate",
"formatter": {
"format": "number",
"chartType": "cross_pivot",
"decimalUnit": 2,
"quantityUnit": "none",
"unitSuffix": "",
"thousandSeparator": false
}
}
],
"sort": [
{
// "field": "yubsrcxz2d1",
"field": "date",
"order": "ascend",
"sortType": "char",
"number": "f6jrvfcdu7y"
}
],
"globalFilter": {
"number": "39nj4tcpcqj",
"rel": "and",
"rules": [
{
"lType": "field",
"lValue": [
"wttvmb5t6ajHeader",
"s3yq11rqfg8"
],
"number": "skipeho6wr1",
"op": "bt",
"rType": "constant",
rValue: monthRange,
/*"rValue": [
// 1696089600000,
// 1698767999999
// new Date('2023/10/1 00:00:00').getTime(),
// new Date('2023/10/31 00:00:00').getTime(),
new Date('2023/12/1 00:00:00').getTime(),
new Date('2023/12/30 00:00:00').getTime(),
]*/
}
]
},
"relatedConfig": {
"targets": []
},
"filterNullFlag": true,
"totalCol": true,
"totalColConfig": {
// "ts0ltoz3dkr": "SUM"
"duration": "SUM"
},
"totalRow": false,
"totalRowConfig": {
// "ts0ltoz3dkr": "SUM"
"duration": "SUM"
},
"showSubTotals": false,
"computedFields": [],
"computedTargets": []
}
},
"context": {}
};
return await $fetch('https://api.yidayun.com/report/previewChart', params).then((res) => {
const data = {
users,
dates: {},
manhours: {},
totals: {}
};
res.data.totalData.forEach((item) => {
data.totals[item.name] = item;
});
res.data.chartData.forEach((item) => {
data.dates[item.date] = item.date;
if (users['user_'+ item.name]) {
data.manhours[item.name +'_'+ item.date] = item;
}
});
// console.log(data);
return data;
});
},
render(data) {
if (!data || !Array.isArray(data.users) || !data.dates || !data.totals) {
return '';
}
// console.log('data.dates:', data.dates);
var item = null;
var table = [];
var thead = [];
var tbody = [];
const users = [ ...data.users ];
// 分组排序
users.sort((a, b) => {
return (a.locale + a.deptName).localeCompare((b.locale + b.deptName));
});
users.forEach((user, rowIndex) => {
user.duration = data.totals[user.name] ? data.totals[user.name].duration || '-' : '-';
// 统计登记工时的天数
user.days = 0;
for (var date in data.dates) {
item = data.manhours[user.name +'_'+ date]
if (item && item.duration !== undefined) {
user.days++;
}
}
if (rowIndex === 0) {
thead.push(`<tr class="y-app-table__manhour-sticky y-app-table__manhour-row-0">`);
thead.push(`<th class="y-app-table__manhour-sticky y-app-table__manhour-col-0"><div class="y-app-table__manhour-cell">姓名</div></th>`);
thead.push(`<th class="y-app-table__manhour-sticky y-app-table__manhour-col-1"><div class="y-app-table__manhour-cell">地点 部门</div></th>`);
thead.push(`<th class="y-app-table__manhour-sticky y-app-table__manhour-col-2"><div class="y-app-table__manhour-cell" title="登记天数合计">天数</div></th>`);
thead.push(`<th class="y-app-table__manhour-sticky y-app-table__manhour-col-3"><div class="y-app-table__manhour-cell" title="工时合计">工时</div></th>`);
}
tbody.push(`<tr>`);
tbody.push(`<td class="y-app-table__manhour-sticky y-app-table__manhour-col-0"><div class="y-app-table__manhour-cell">${ user.name }</div></td>`);
tbody.push(`<td class="y-app-table__manhour-sticky y-app-table__manhour-col-1"><div class="y-app-table__manhour-cell">${ user.locale } ${ user.deptName }</div></td>`);
tbody.push(`<td class="y-app-table__manhour-sticky y-app-table__manhour-col-2"><div class="y-app-table__manhour-cell">${ user.days }</div></td>`);
tbody.push(`<td class="y-app-table__manhour-sticky y-app-table__manhour-col-3"><div class="y-app-table__manhour-cell">${ user.duration }</div></td>`);
for (var date in data.dates) {
item = data.manhours[user.name +'_'+ date] || {};
item.d = new Date(date.replace(/-/g, '/'));
item.dStr = date;
item.date = item.d.getDate();
item.dateStr = item.date.toString().padStart(2, 0);
item.month = item.d.getMonth() + 1;
item.monthStr = item.month.toString().padStart(2, 0);
item.year = item.d.getFullYear();
item.day = item.d.getDay();
item.dayStr = ['日','一','二','三','四','五','六'][item.day];
item.isWeekend = item.day === 0 || item.day === 6;
item.duration = item.duration || '-';
item.unqualified = !item.isWeekend && item.duration !== '-' && parseFloat(item.duration) < 8;
if (rowIndex === 0) {
thead.push(`<th class="${ item.isWeekend ? 'is-weekend' : '' }"><div class="y-app-table__manhour-cell" title="${ item.year }年${ item.month }月${ item.date }日 (星期${ item.dayStr })">${ item.dStr } </div></th>`);
}
tbody.push(`<td class="${ item.isWeekend ? 'is-weekend' : '' }${ item.unqualified ? ' is-unqualified' : '' }"><div class="y-app-table__manhour-cell" title="${ item.year }年${ item.month }月${ item.date }日 (星期${ item.dayStr })">${ item.duration }</div></td>`);
// console.log('item:', item);
}
if (rowIndex === 0) {
thead.push(`<\/tr>`);
}
tbody.push(`<\/tr>`);
});
if (thead.length) {
thead.unshift('<thead>');
thead.push('</thead>');
table.push(thead.join(''));
}
if (tbody.length) {
tbody.unshift('<tbody>');
tbody.push('</tbody>');
table.push(tbody.join(''));
}
if (table.length) {
table.unshift('<table class="y-app-table__manhour">');
table.push('</table>');
}
return table.join('');
},
};
constructor() {
console.log(`[OK] 初始化 ...`);
this.#VIEWS.forEach((item) => {
this.#VIEWS[item.object] = item;
this.setState({[item.object]: item.checked});
});
this.loadState();
this.init();
this.BOOKMARKS = new BOOKMARKS();
if (window.top !== window) {
this.PLAYERS = new PLAYERS();
this.BTN_SHARE = new BTN_SHARE();
}
}
// @ api
getUserInfo(success) {
return new Promise((resolve, reject) => {
$fetch('https://api.yidayun.com/account/get-session-info', {
context: {},
params: {}
}).then((res) => {
const data = res.data ? res.data : res;
if (data && data.account) {
data.id = data.user.id;
data.name = data.user.name;
data.number = data.user.number;
data.phone = data.user.phone;
data.companyName = data.workspace.name;
data.key = `${ data.workspace.number }-${ data.token }`;
data.keyTab = `${ data.key }-CUR_TAB`;
data.keyTabs = `${ data.key }-TABS`;
USER_INFO.set(data);
if (typeof success === 'function') {
success(data, res);
}
resolve(data);
} else {
console.error(`account info error`, res);
reject(res);
}
}).catch((err) => {
console.warn('[WARN]', err);
reject(err);
});
});
}
getManhour() {
return new Promise((resolve, reject) => {
const userName = USER_INFO.get('name');
const userNumber = USER_INFO.get('number');
const view = this.#VIEW_MANHOUR;
const now = new Date();
const data = {
userName,
userNumber,
today: 0,
yestoday: 0
};
// data.todayTime = `${ now.getFullYear() }${ (now.getMonth()+1).toString().padStart(2, 0) }${ now.getDate().toString().padStart(2, 0) }`;
data.todayTime = `${ now.getFullYear() }${ (now.getMonth()+1) }${ now.getDate() }`;
now.setDate(now.getDate()-1);
// data.yestodayTime = `${ now.getFullYear() }${ (now.getMonth()+1).toString().padStart(2, 0) }${ now.getDate().toString().padStart(2, 0) }`;
data.yestodayTime = `${ now.getFullYear() }${ (now.getMonth()+1) }${ now.getDate() }`;
$fetch('https://api.yidayun.com/runtime/getList', view.params({ userName })).then((res) => {
const records = res.data?.records || [];
let userNumber = '';
let workdate = '';
records.forEach((item) => {
userNumber = item[view.object +'Header_creatorNumber'];
if (data.userNumber === userNumber) {
workdate = new Date(item[view.object +'Header_f_g29exe']).toLocaleString('zh-CN', {hour12: false}).replace(/\//g,'').split(' ')[0];
// console.log('workdate', item[view.object +'Header_f_g29exe'], workdate, 'Header_f_nssifb', item[view.object +'Header_f_nssifb']);
if (workdate === data.todayTime) {
data.today = parseFloat(data.today) + parseFloat(item[view.object +'Header_f_nssifb']);
}
if (workdate === data.yestodayTime) {
data.yestoday = parseFloat(data.yestoday) + parseFloat(item[view.object +'Header_f_nssifb']);
}
}
});
// console.log('manhour:', data);
resolve(data);
}).catch((error) => {
console.error('[ERROR]', error);
reject(data);
});
});
}
getShareLink(query) {
if (typeof query !== 'object') {
return Promise.reject('params error');
}
return $fetch('https://api.yidayun.com/share/getShareLink', {
context: {},
params: { objectNumber: query.object, dataId: query.resId }
}).then((res) => {
query.url = res.data;
query.resId = query.resId;
query.objectNumber = query.object;
return query;
}).catch((err) => {
console.warn('[WARN]', err);
return err;
})
}
// @ utils
getRecordByElement = ($el, success, error) => {
const VIEWS = this.#VIEWS;
const BOOKMARKS = this.BOOKMARKS;
const getRecord = ($el) => {
const data = { status: true, message: 'ok' };
if (!$el) {
data.status = false;
data.message = '[ERROR] element not found';
console.error(data.message);
return data;
}
data.from = $el.dataset.from;
data.action = $el.dataset.action;
if (data.action.indexOf('.') > -1) {
data.action = data.action.split('.')[1] || '';
}
data.object = $el.dataset.object;
data.index = $el.dataset.index;
if (data.index !== undefined) {
data.index = parseInt(data.index);
}
// console.log('y-app-action:', data);
if (data.object === undefined || data.index === undefined) {
data.status = false;
data.message = `[ERROR] 参数异常: object: ${ data.object }, index: ${ data.index }`;
console.error(data.message);
return data;
}
if (data.from === 'search') {
data.record = VIEWS[data.object].records[data.index];
} else {
data.record = BOOKMARKS.get(data.object, data.index);
}
return data;
}
if (typeof success === 'function') {
const data = getRecord($el);
if (data.status) {
success(data);
} else {
if (typeof error === 'function') {
error(data);
}
}
return data;
} else {
return new Promise((resolve, reject) => {
const data = getRecord($el);
if (data.status) {
resolve(data);
} else {
reject(data);
}
});
}
return new Promise((resolve, reject) => {
let message = 'record not found';
let record = null;
const from = $el.dataset.from;
const action = $el.dataset.action;
const object = $el.dataset.object;
let index = $el.dataset.index;
if (index !== undefined) {
index = parseInt(index);
}
// console.log('y-app-action:', from, action, object, index);
if (object === undefined || index === undefined) {
message = `[ERROR] 参数异常: object: ${ object }, index: ${ index } `
console.error(message);
return reject({ message });
}
if (from === 'search') {
record = VIEWS[object].records[index];
} else {
record = BOOKMARKS.get(object, index);
}
if (typeof record === 'object') {
if (typeof callback === 'function') {
callback(record);
}
return resolve(record);
}
return reject({ message });
});
}
getWorkingHours(options) {
const defaultOptions = {
interval: 0,
prefix: undefined,
duration: '',
before() {},
after() {}
}
options = Object.assign({}, defaultOptions, options);
const base = new Date();
base.setHours(9);
base.setMinutes(0);
base.setSeconds(0);
base.setMilliseconds(0);
const getNow = () => {
return new Date();
// return new Date('2023/10/19 13:10:12');
// return new Date('2023/10/19 12:30:12');
}
const current = new Date();
if (base.getHours() - current.getHours() > 2) {
base.setDate(base.getDate()-1);
}
const calc = function() {
if (typeof options.before === 'function') {
options.before(options, this);
}
const now = getNow();
const start = new Date(base.getTime());
// reset start time
start.setMinutes(0);
start.setSeconds(0);
start.setMilliseconds(0);
// 9点 上班
if (options.start == '9:00') {
start.setMinutes(0);
}
// 9点30分 上班
if (options.start == '9:30') {
start.setMinutes(30);
}
// 午餐时间 不计时
if (now.getHours() == 12) {
now.setHours(12);
now.setMinutes(0);
now.setSeconds(0);
now.setMilliseconds(0);
}
// 13点开始上班,减去 午餐时间 1小时
if (now.getHours() > 12) {
now.setMinutes(now.getMinutes() - 60);
}
let diffText = [];
if (start < now) {
var diff = now.getTime() - start.getTime();
const days = Math.floor(diff / (24 * 3600 * 1000));
// 时
diff = diff % (24 * 3600 * 1000);
const hours = Math.floor(diff / (3600 * 1000));
// 分
diff = diff % (3600 * 1000);
const minutes = Math.floor(diff / (60 * 1000));
// 秒
diff = diff % (60*1000);
const seconds = Math.round(diff / 1000);
diffText.push(hours +'小时');
diffText.push(minutes +'分钟');
diffText.push(seconds +'秒');
diffText = `${ (options.prefix || '') }${ diffText.join(' ') }`;
} else {
diffText = '上班时间:'+ start.toLocaleTimeString();
}
options.duration = diffText;
if (typeof options.after === 'function') {
options.after(options, this);
}
}
calc();
if (options.interval) {
setInterval(function() {
calc();
}, options.interval);
}
}
// @ methods
// 注入全局样式
injectGlobalStyle() {
const cssRules = () => {
return `
body>svg{
position: absolute;
width: 0px;
height: 0px;
overflow: hidden;
}
/* reset layout */
#y-app ~ #root{
padding-top: 80px;
}
#y-app ~ #root .ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed{
top: 80px !important;
}
/* 调整分享详情页 高度 */
#y-app ~ #root .eb-share-form-container{
height: calc(100vh - 80px) !important;
}
/* 调整左侧菜单高度 */
#y-app ~ #root .ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed{
height: calc(100vh - 80px) !important;
}
${ window.top !== window ? `
/* iframe 嵌入时 */
${ window.location.search.indexOf('sb=0') > 0 ? `
/* 调整 容器 背景色 */
#root .ant-pro-basicLayout>.ant-layout .eb-layout-container{
background-color: #fff !important;
}
/* 隐藏侧边栏 */
#root .ant-pro-basicLayout>.ant-layout>.ant-layout-sider,
#root .ant-pro-basicLayout>.ant-layout>div:not(.ant-layout){
display: none !important;
}
/* 顶部标签 */
/* 隐藏右键菜单 关闭所有标签 */
.ant-dropdown-menu-item-only-child[data-menu-id^="rc-menu-uuid-"][data-menu-id$="-closeAll"]{
display: none;
}
/* 隐藏首页 tab 标签 */
.eb-layout-container-section>.ant-tabs>.ant-tabs-nav .ant-tabs-tab:not(.ant-tabs-tab-with-remove){
border: 1px solid red;
display: none !important;
}
/* 隐藏首页 tab 内容 */
.ant-tabs-tabpane[id$="-panel-tabhome"][id^="rc-tabs-"]{
display: none !important;
}
` : ''}
/* 调整 右侧主题内容 内边距 */
#root .ant-pro-basicLayout>.ant-layout .eb-layout-container-section{
margin: 20px 10px 10px 10px;
}
/* 调整 tab 标签页内容 内边距 */
#root .ant-pro-basicLayout>.ant-layout .eb-home-tabs-tab{
padding: 0 0 20px 0 !important;
}
/* 调整 tab 标签 上边距 */
#root .eb-home-tabs>.ant-tabs-nav .ant-tabs-nav-wrap{
padding-top: 3px;
}
/* 详情页 调整右侧边栏宽度 */
#root .eb-view-wrapper .eb-view-sider-right{
flex: 0 0 300px;
width: 300px;
margin-right: 10px;
}
/* 详情页 调整 右侧审批流 外左边距 */
#root .eb-view-wrapper .eb-view-sider-right .eb-flow{
margin-left: -10px;
}
/* 详情页 调整 内容部分 内边距 */
#root .eb-view-wrapper>.eb-view-form-wrapper>.eb-view-form>.eb-view-content,
#root .eb-view-wrapper>.eb-view-form-wrapper>.eb-view-form>.eb-view-list-wrapper{
padding: 0 10px;
}
/* 详情页 调整 tab 内边距 */
#root .eb-view-wrapper>.eb-view-form-wrapper>.eb-view-form>.eb-view-toolbar{
margin-left: 0;
margin-right: 0;
padding-left: 10px;
padding-right: 10px;
}
/* 详情页 隐藏 上查、下查 */
#root .eb-view-toolbar.eb-view-toolbar-form-head>.ant-space.ant-formily-button-group>.ant-space-item>.ant-space.ant-space-horizontal{
display: none !important;
}
/* 调整 列表页 顶部 试图切换 tab 大小 */
.rc-overflow.eb-view-plans .eb-view-plans-item{
padding-top: 2px !important;
padding-bottom: 2px !important;
margin-bottom: 0 !important;
}
` : '' }
/* 重置按钮文字 字重 */
.ant-btn{
font-weight: 500 !important;
}
/* 重置详情页 当前激活标签样式 */
.ant-tabs-content-top .ant-tabs-tab{
padding-top: 6px !important;
padding-bottom: 6px !important;
padding-left: 10px !important;
padding-right: 10px !important;
}
.eb-view-toolbar-tabs.ant-tabs>.ant-tabs-nav .ant-tabs-ink-bar{
height: 4px !important;
bottom: 0px !important;
}
.eb-view-toolbar-tabs.ant-tabs>.ant-tabs-nav .ant-tabs-tab+.ant-tabs-tab {
margin: 0 0 0 15px !important;
}
.eb-view-toolbar-tabs.ant-tabs>.ant-tabs-nav .ant-tabs-tab-active{
font-size: 14px !important;
}
.ant-pro-sider-logo{
align-items: start !important;
}
/* 重置详情页表单label */
.eb-view-form .ant-formily-item-layout-vertical .ant-formily-item-label{
color: #666 !important;
margin-bottom:1px !important;
}
/* 详情页富文本内容预览/编辑器行高 */
.form-html-editor-preview p,
.form-html-editor.form-html-editor-edit .w-e-text-container>.w-e-scroll>div>p{
margin: 0 0 2px 0 !important;
line-height: 1.5;
}
/* 详情页右侧流程 标题文字大小调整 */
.eb-flow-item-node-title .ant-typography-ellipsis,
.eb-flow-item-node-title-assign{
font-size: inherit !important;
}
/* 评论和日志字重调整 */
.eb-comment-list-item-content,
.eb-form-sider-log-item-time,
.eb-form-sider-log-item-content{
font-weight: inherit !important;
}
/* 日志 */
.eb-form-sider-log-item{
padding-top: 5px !important;
padding-bottom: 5px !important;
}
.eb-form-sider-log-item-time{
padding-bottom: 0 !important;
}
.eb-form-sider-log-item-content{
margin-bottom: 0 !important;
}
/* textarea 最小高度调整 */
.ant-formily-item textarea.ant-input{
min-height: 180px;
}
/* 重置详情页底部操作按钮位置,改到左边展示 */
.eb-view-toolbar--bottom .eb-view-toolbar-group{
direction: ltr !important;
}
.eb-view-toolbar--bottom .eb-view-toolbar-group .eb-view-toolbar-overflow-right{
justify-content: left !important;
}
/* 消息列表样式 */
.eb-message-center-list-item .ant-list-item-meta-title{
margin-bottom: 0 !important;
}
.eb-message-center-list-item-time{
margin-bottom: 0 !important;
margin-top: 0 !important;
font-weight: inherit !important;
}
.eb-message-center-list-item .ant-list-item-meta-description .ant-typography{
font-weight: inherit !important;
}
/* 移除 联系人左边的小图标 */
.eb-perview-option-tag>.ant-typography-ellipsis{
max-width: none !important;
margin-left: auto !important;
}
.eb-perview-option-tag>.anticon{
display: none !important;
}
`.trim();
};
const $style = document.createElement('style');
$style.setAttribute('id', 'y-app-style-global');
$style.setAttribute('type', 'text/css');
$style.innerHTML = cssRules();
document.head.appendChild($style);
console.log(`[OK] 初始化 注入全局样式`);
}
// 初始化主视图
initMainView(callback) {
const state = this.state;
const VIEWS = this.#VIEWS;
const PAGE_SIZES = this.#PAGE_SIZES;
const BOOKMARKS = this.BOOKMARKS;
const styles = () => {
return `
<style type="text/css">
.y-app-root,
.y-app-root *,
.y-app-root *:before,
.y-app-root *:after{
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
.y-app-root{
--font-family: -apple-system,"PingFang SC","Microsoft YaHei","Helvetica Neue",Helvetica,BlinkMacSystemFont,"Segoe UI","Hiragino Sans GB",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
--font-size: 75%;
--color: #333;
--background: #fff;
--background-hover: #F6F6F6;
--background-active: #F2F5F9;
--link-color: #348fe4;
--h2-background: #F1F1F1;
--button-border-color: #e8e8e8;
--button-border-color-hover: #e8e8e8;
--button-background: #fff;
--button-background-hover: #f5f5f5;
--head-height: 80px;
--sidebar-width: 220px;
--bookmarks-width: 220px;
--link-color: #348fe4;
}
a{
color: var(--link-color);
text-decoration: none;
}
a:hover{
text-decoration: underline;
}
.y-app-hidden{
display: none !important;
}
.y-app-hidden-v{
visibility: hidden !important;
}
a.y-app-btn,
a.y-app-btn:hover,
a.y-app-btn:focus,
a.y-app-btn:active{
text-decoration: none;
}
.y-app-btn{
display: inline-block;
border-width: 1px;
border-style: solid;
border-color: var(--button-border-color);
background-color: var(--button-background);
color: var(--color);
padding: 5px 15px;
border-radius: 3px;
cursor: pointer;
font-size: inherit;
& + .y-app-btn{
margin-left: 8px;
}
&:hover{
box-shadow: 0 3px 8px 0 rgba(0,0,0, 0.06);
border-color: var(--button-border-color-hover);
background: var(--button-background-hover);
}
&:active{
box-shadow: 0 2px 4px 0 rgba(0,0,0, 0.06);
background: rgba(0, 0, 0, 0.06);
}
&[primary]{
color: #5186f0;
}
&[warn]{
color: #f98e1b;
}
&[danger]{
color: #f52743;
}
&.active{
color: #fff;
background-color: #5186f0;
}
&.success{
color: #fff;
background-color: #5eba7d;
}
/* y-app-btn-icon */
&.y-app-btn-icon{
border: 0;
box-shadow: none;
padding: 4px 5px;
height: auto;
line-height: 0;
text-align: center;
display: inline-block;
vertical-align: middle;
& svg {
pointer-events: none;
fill: #595959;
&.svg-icon-starred{
fill: #eac54f;
}
&.svg-icon-check{
fill: #28c940;
}
}
}
}
.text-center{
text-align: center;
}
.pull-left{
float: left;
}
.pull-right{
float: right;
}
.y-app-root{
margin: 0;
color: rgba(0,0,0,.85);
font-size: 14px;
font-family: -apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;
font-variant: tabular-nums;
line-height: 1.5715;
background-color: #fff;
font-feature-settings: "tnum";
}
.y-app-head{
position: fixed;
left: 0;
top: 0;
right: 0;
z-index: 999;
height: var(--head-height);
background-color: #fff;
background: rgba(255,255,255,0.85);
background: #f9f9f9;
border-bottom: 1px solid #d5d5d5;
box-shadow: 0 2px 10px rgba(0,0,0,.15);
}
.y-app-main{
position: absolute;
top: 0;
right: 0;
left: 0;
z-index: 888;
}
.y-app-padded{
padding: 10px;
}
.y-app-logo{
position: absolute;
top: 0;
left: 0;
margin: 0;
padding: 2px 0 0 10px;
}
.y-app-search{
display: inline-block;
vertical-align: top;
margin: 0;
padding-top: 10px;
padding-left: 15px;
.y-app-search__input{
min-width: 467px;
}
.y-app-search__submit{
margin-left: 10px;
}
.y-app-search__options{
padding-top: 6px;
padding-right: 20px;
display: inline-block;
vertical-align: top;
}
}
.y-app-input,
.y-app-btn{
display: inline-block;
vertical-align: middle;
}
.y-app-input{
height: 32px;
line-height: 32px;
padding: 0 5px;
border: 1px solid #ccc;
}
.y-app-btn{
height: 32px;
line-height: 32px;
padding: 0 15px;
cursor: pointer;
border-radius: 3px;
border: 1px solid #ccc;
}
.y-app-checkbox,
.y-app-checkbox__input,
.y-app-checkbox__text
.y-app-radio,
.y-app-radio__input,
.y-app-radio__text{
display: inline-block;
}
.y-app-checkbox,
.y-app-radio{
vertical-align: top;
padding-right: 10px;
}
.y-app-checkbox__input,
.y-app-checkbox__text,
.y-app-radio__input,
.y-app-radio__text{
vertical-align: middle;
}
.y-app-checkbox__text,
.y-app-radio__text{
padding-left: 1px;
}
.y-app-radio__input{
margin: 3px 2px 3px 3px;
}
.y-app-action .y-app-checkbox__input,
.y-app-action .y-app-checkbox__text,
.y-app-action .y-app-radio__input,
.y-app-action .y-app-radio__text{
pointer-events: none;
}
@keyframes drawerFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.y-app-drawer{
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 999;
&.active{
.y-app-drawer__backdrop{
height: 100%;
opacity: 1;
-webkit-transition: none;
transition: none;
-webkit-animation: drawerFadeIn .3s cubic-bezier(.23,1,.32,1);
animation: drawerFadeIn .3s cubic-bezier(.23,1,.32,1);
}
.y-app-drawer__container{
-webkit-transform: translateX(0);
transform: translateX(0);
}
}
.y-app-drawer__backdrop{
height: 0;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 55;
background: rgba(0,0,0,.45);
opacity: 0;
-webkit-transition: opacity .3s linear,height 0s ease .3s;
transition: opacity .3s linear,height 0s ease .3s;
}
.y-app-drawer__container{
box-shadow: -6px 0 16px -8px rgba(0,0,0,.08), -9px 0 28px rgba(0,0,0,.05), -12px 0 48px 16px rgba(0,0,0,.03);
position: absolute;
top: 0;
right: 0;
bottom: 0;
z-index: 99;
width: 80%;
max-width: 1200px;
min-width: 980px;
-webkit-transform: translateX(100%);
transform: translateX(110%);
-webkit-transition: box-shadow .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);
transition: box-shadow .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);
transition: transform .3s cubic-bezier(.23,1,.32,1),box-shadow .3s cubic-bezier(.23,1,.32,1);
transition: transform .3s cubic-bezier(.23,1,.32,1),box-shadow .3s cubic-bezier(.23,1,.32,1),-webkit-transform .3s cubic-bezier(.23,1,.32,1);
}
.y-app-drawer__head{
height: 50px;
background-color: #efeff4;
position: relative;
z-index: 77;
}
.y-app-drawer__title{
font-size: 14px;
margin: 0;
padding-left: 110px;
padding-top: 15px;
display: inline-block;
text-overflow: ellipsis;
overflow: hidden;
max-width: 95%;
word-break: keep-all;
white-space: nowrap;
}
.y-app-drawer__btn-close{
position: absolute;
top: 0;
left: 0;
bottom: 0;
border: 0;
border-radius: 0;
padding-left: 8px;
padding-right: 8px;
min-height: 50px;
min-width: 54px;
line-height: normal;
}
.y-app-btn__copy{
position: absolute;
top: 50%;
left: 60px;
transform: translateY(-50%);
}
.y-app-drawer__main{
position: relative;
z-index: 77;
background: #fff;
height: calc(100vh - 50px);
}
.y-app-drawer__iframe{
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
width: 100%;
height: 100%;
}
}
.y-app-search__bookmarks{
position: fixed;
top: var(--head-height);
bottom: 0;
left: 0;
z-index: 9;
width: var(--bookmarks-width);
background: #eee;
overflow: hidden;
&:hover{
width: 560px;
overflow: visible;
}
}
.y-app-search__bookmarks-container{
min-width: 1200px;
overflow: auto;
background: #fff;
}
.y-app-search__result{
/* margin-left: var(--bookmarks-width); */
padding: 15px;
}
/* 工时筛选 */
.y-app-manhour__filter{
position: fixed;
top: var(--head-height);
left: 0;
right: 20px;
z-index: 9;
height: 60px;
padding-top: 18px;
padding-left: 20px;
padding-right: 20px;
background: #fff;
}
.y-app-manhour__filter-item{
display: inline-block;
vertical-align: middle;
padding-right: 15px;
}
.y-app-manhour__filter-item .y-app-radio{
padding-right: 0;
}
.y-app-manhour__result{
position: relative;
z-index: 3;
margin-top: 60px;
padding: 0 20px 20px 20px;
}
/* 工时表格 */
.y-app-table__manhour{
table-layout: fixed;
border-collapse: collapse;
border-spacing: 0;
font-size: 14px;
}
.y-app-table__manhour,
.y-app-table__manhour th,
.y-app-table__manhour td{
padding: 0;
}
.y-app-table__manhour th,
.y-app-table__manhour td{
white-space: nowrap;
min-width: 40px;
line-height: 20px;
}
.y-app-table__manhour th small,
.y-app-table__manhour td small{
font-weight: normal;
}
.y-app-table__manhour th:not(:first-child),
.y-app-table__manhour td:not(:first-child){
text-align: center;
}
.y-app-table__manhour th{
background-color: #eee;
}
.y-app-table__manhour td{
background-color: #fff;
}
.y-app-table__manhour tr:hover td{
background-color: #fff1c9;
}
.y-app-table__manhour-cell{
padding: 3px 5px 2px 5px;
border: 1px solid #ccc;
margin: -1px;
}
.y-app-table__manhour-sticky{
position: sticky;
}
.y-app-table__manhour-sticky.y-app-table__manhour-row-0{
top: 81px;
top: 0px;
top: 61px;
z-index: 7;
}
.y-app-table__manhour-sticky.y-app-table__manhour-col-0{
left: 21px;
left: 1px;
min-width: 70px;
max-width: 70px;
}
.y-app-table__manhour-sticky.y-app-table__manhour-col-1{
left: 91px;
left: 71px;
min-width: 130px;
max-width: 130px;
text-align: left !important;
}
.y-app-table__manhour-sticky.y-app-table__manhour-col-2{
left: 222px;
left: 202px;
min-width: 60px;
max-width: 60px;
background-color: #ffeed4;
background-color: #d8eeff;
}
.y-app-table__manhour-sticky.y-app-table__manhour-col-3{
left: 222px;
left: 263px;
min-width: 60px;
max-width: 60px;
background-color: #ffeed4;
background-color: #ffeaca;
}
.y-app-table__manhour .is-weekend{
background-color: #d1ffd1;
background-color: #e2ffe2;
}
.y-app-table__manhour .is-unqualified{
color: red;
font-weight: bold;
}
.y-app-table{
width: 100%;
border-collapse: collapse;
font-size: inherit;
}
.y-app-table + .y-app-table{
margin-top: 25px;
}
.y-app-table__caption{
font-weight: bold;
font-size: 14px;
background: #F1F1F1;
background: rgba(241,241,241, 0.88);
padding: 5px 10px;
text-align: left;
position: sticky;
top: 0;
margin-bottom: 5px;
}
.y-app-table tr:hover>td{
background: var(--background-hover);
}
.y-app-table tr:active>td{
background: var(--background-active);
}
.y-app-table th,
.y-app-table td {
padding: 3px 5px;
}
/* y-app-view */
.y-app-view{
position: absolute;
top: 0;
padding-top: var(--head-height);
right: 0;
bottom: 0;
left: 0;
min-height: 100vh;
z-index: 888;
overflow: auto;
background: #fff;
display: none;
&.active{
display: block;
}
}
.y-app-view__container{
height: 100%;
}
.y-app-view__iframe{
width: 100%;
height: 100%;
}
.y-app-nav{
display: inline-block;
vertical-align: top;
padding-top: 48px;
.y-app-nav__item{
display: inline-block;
padding: 5px 10px;
min-width: 80px;
text-align: center;
border-radius: 3px 3px 0 0;
&:hover{
background: rgba(0,0,0,.05);
text-decoration: none;
}
&:active{
background: rgba(0,0,0,.1);
text-decoration: none;
}
&.active{
color: #fff;
background: #0075ff;
background: #2b90ff;
}
}
}
/* 右上角工时 */
.y-app-manhour{
position: fixed;
top: -1px;
right: 0;
height: var(--head-height);
z-index: 1999;
overflow: hidden;
min-width: 228px;
padding: 4px 10px 10px 10px;
background: rgba(255,255,255, 0.88);
background: #f9f9f9;
background: rgba(249,249,249, 0.9);
border-bottom: 1px solid transparent;
border-left: 1px solid transparent;
transition: all .3s cubic-bezier(.23,1,.32,1);
}
.y-app-manhour:hover{
height: auto;
overflow: visible;
background: inherit;
box-shadow: -6px 0 16px -8px rgba(0,0,0,.08), -9px 0 28px rgba(0,0,0,.05), -12px 0 48px 16px rgba(0,0,0,.03);
border-bottom: 1px solid #d5d5d5;
border-left: 1px solid #d5d5d5;
box-shadow: 0 2px 10px rgba(0,0,0,.15);
}
.y-app-manhour__detail,
.y-app-manhour__detail>li{
list-style: none;
margin: 0;
padding: 0;
}
.y-app-manhour__countdown{
float: right;
}
.y-app-manhour__detail{
line-height: 18px;
}
.y-app-manhour__extra{
display: none;
}
.y-app-manhour__extra,
.y-app-manhour__extra>dt,
.y-app-manhour__extra>dd{
list-style: none;
margin: 0;
padding: 0;
}
.y-app-manhour__extra{
padding-top: 10px;
}
.y-app-manhour__extra>dt{
font-weight: bold;
}
.y-app-manhour:hover>.y-app-manhour__extra{
display: block;
}
.y-app-h2{
font-size: 32px;
padding: 50px 0;
text-align: center;
color: #666;
}
.y-app-italic{
font-style: italic;
}
</style>
`.trim();
};
const template = () => {
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth() + 1;
return `
${ styles() }
<div class="y-app-head">
<h2 class="y-app-logo"><a href="/home.html">易用云</a></h2>
<nav class="y-app-nav" id="y-app-nav">
<a href="#/home" class="y-app-nav__item y-app-action" data-action="prevent.switchView" data-view="home">易搭云</a>
<a href="#/bookmarks" class="y-app-nav__item y-app-action" data-action="prevent.switchView" data-view="bookmarks">收藏夹</a>
<a href="#/search" class="y-app-nav__item y-app-action" data-action="prevent.switchView" data-view="search">需求/缺陷/任务</a>
<a href="#/manhour" class="y-app-nav__item y-app-action" data-action="prevent.switchView" data-view="manhour">工时</a>
</nav>
<form class="y-app-search" id="y-app-search-form" onsubmit="return false">
<div class="y-app-search__inputbox">
<input type="text" id="y-app-search-keyword" class="y-app-input y-app-search__input" placeholder="需求/缺陷/任务关键字或姓名">
<button type="submit" class="y-app-btn y-app-search__submit" primary>搜索</button>
</div>
<div class="y-app-search__options">
${ VIEWS.map((item) => {
return `
<label class="y-app-checkbox y-app-action" data-action="checkbox">
<input type="checkbox" name="y-app-search-types" class="y-app-checkbox__input" value="${ item.object }" ${ state.types[item.object] ? ` checked` : '' }>
<span class="y-app-checkbox__text">${ item.name }</span>
</label>`;
}).join('') }
</div>
<div class="y-app-search__options">
<label class="y-app-checkbox y-app-action" data-action="checkbox" title="我创建/负责/参与的">
<input type="checkbox" class="y-app-checkbox__input" value="involved" ${ state.involved ? ` checked` : '' }>
<span class="y-app-checkbox__text">与我有关</span>
</label>
</div>
<div class="y-app-search__options">
<select id="y-app-search-size">
${ PAGE_SIZES.map((item) => {
return `<option value="${ item.value }" ${ state.pagesize == item.value ? ` selected="selected"` : '' }>${ item.value }条</option>`.trim();
}).join('') }
</select>
</div>
</form>
</div>
<div class="y-app-manhour" id="y-app-manhour">
<ul class="y-app-manhour__detail"></ul>
<dl class="y-app-manhour__extra">
<dt>设置</dt>
<dd>上班时间:
<label title="上午 9点" class="y-app-radio y-app-action" data-action="prevent.setWorkTime" data-value="9:00">
<input type="radio" class="y-app-radio__input" name="y-app-manhour-time" value="9:00" ${ state.worktime == '9:00' ? ` checked="checked"` : '' }>
<span class="y-app-radio__text">9:00</span>
</label>
<label title="上午 9点30分" class="y-app-radio y-app-action" data-action="prevent.setWorkTime" data-value="9:30">
<input type="radio" class="y-app-radio__input" name="y-app-manhour-time" value="9:30" ${ !state.worktime || state.worktime == '9:30' ? ` checked="checked"` : '' }>
<span class="y-app-radio__text">9:30</span>
</label>
</dd>
</dl>
<dl class="y-app-manhour__extra">
<dt>时长计算说明</dt>
<dd>13点后 减去午餐时间 1小时</dd>
</dl>
</div>
<div class="y-app-main">
<div class="y-app-view y-app-view__search" data-view="bookmarks">
<div class="y-app-search__result" id="y-app-bookmarks">
</div>
</div>
<div class="y-app-view y-app-view__search" data-view="search">
<div class="y-app-search__result" id="y-app-search-result">
<h2 class="y-app-h2 y-app-italic"> SUBLIME / SIMPLE / SMART </h2>
<p class="text-center">
<button type="button" class="y-app-btn y-app-action" data-action="prevent.load" primary>瞧一瞧 ~</button>
</p>
</div>
</div>
<div class="y-app-view y-app-view__manhour" data-view="manhour">
<div class="y-app-manhour__filter">
<span class="y-app-manhour__filter-item">
<select id="y-app-manhour-year">
${ [2024,2023,2022].map((item) => {
return `<option value="${ item }"${ currentYear == item ? ` selected` : '' }>${ item }年</option>`;
}).join('') }
</select>
</span>
<span class="y-app-manhour__filter-item">
<select id="y-app-manhour-month">
${ [12,11,10,9,8,7,6,5,4,3,2,1].map((item) => {
return `<option value="${ item }"${ currentMonth == item ? ` selected` : '' }>${ item }月</option>`;
}).join('') }
</select>
</span>
<span class="y-app-manhour__filter-item">
<label class="y-app-radio" data-action="getManhour">
<input type="radio" class="y-app-radio__input" name="y-app-manhour-group-by" value="me" checked>
<span class="y-app-radio__text">我的</span>
</label>
</span>
<span class="y-app-manhour__filter-item">
<label class="y-app-radio">
<input type="radio" class="y-app-radio__input" name="y-app-manhour-group-by" value="all">
<span class="y-app-radio__text">全部</span>
</label>
</span>
<span class="y-app-manhour__filter-item">
<label class="y-app-radio">
<input type="radio" class="y-app-radio__input" name="y-app-manhour-group-by" value="上海">
<span class="y-app-radio__text">上海</span>
</label>
</span>
<span class="y-app-manhour__filter-item">
<label class="y-app-radio">
<input type="radio" class="y-app-radio__input" name="y-app-manhour-group-by" value="杭州">
<span class="y-app-radio__text">杭州</span>
</label>
</span>
<span class="y-app-manhour__filter-item">
<label class="y-app-radio">
<input type="radio" class="y-app-radio__input" name="y-app-manhour-group-by" value="苏州">
<span class="y-app-radio__text">苏州</span>
</label>
</span>
<span class="y-app-manhour__filter-item">
<button type="button" class="y-app-btn y-app-action" primary data-action="prevent.viewManhours">查看</button>
</span>
<span class="y-app-manhour__filter-item">
<button type="button" class="y-app-btn y-app-action" data-action="prevent.copyManhours" title="复制报表">复制</button>
</span>
</div>
<div class="y-app-manhour__result" id="y-app-manhour-result">
<h2 class="y-app-h2 y-app-italic"> 成员工时统计 </h2>
</div>
</div>
</div>
`.trim();
}
var $app = document.createElement('y-app');
$app.setAttribute('id', 'y-app');
if (document.body.children.length) {
document.body.insertBefore($app, document.body.children[0]);
} else {
document.body.appendChild($app);
}
$app.$shadow = $app.attachShadow({ mode: 'open' });
$app.$root = document.createElement('div');
$app.$root.className = 'y-app-root';
$app.$root.innerHTML = template().trimHTML();
// $app.$root.addEventListener('click', (event) => {
// event.stopPropagation();
// });
// 工时报表视图
$app.$manhourYear = $app.$root.querySelector('#y-app-manhour-year');
$app.$manhourMonth = $app.$root.querySelector('#y-app-manhour-month');
$app.$manhourResult = $app.$root.querySelector('#y-app-manhour-result');
$app.getManhoursFormData = () => {
const data = {};
data.year = $app.$manhourYear.value;
data.month = $app.$manhourMonth.value;
data.groupby = $app.$root.querySelector('input[type="radio"][name="y-app-manhour-group-by"]:checked')?.value;
return data;
}
// 工时
$app.$manhour = $app.$root.querySelector('#y-app-manhour');
$app.$manhour.$detail = $app.$manhour.querySelector('.y-app-manhour__detail');
$app.updateManhour = () => {
const VIEW_MANHOUR = this.#VIEW_MANHOUR;
const countdownValue = 30; // 30秒倒计时 循环
const data = {
countdown: countdownValue,
today: 0,
yestoday: 0,
now: '',
duration: '',
};
// 拉取已登记的工时
const getManhour = () => {
this.getManhour().then((res) => {
// console.log('getManhour:', res);
data.today = res.today;
data.yestoday = res.yestoday;
$app.$manhour.$detail.innerHTML = VIEW_MANHOUR.render(data);
});
}
// 计算上班时长
this.getWorkingHours({
interval: 1000,
prefix: '时长:',
before: (options) => {
// console.log('countdown:', data.countdown);
data.countdown--;
options.start = state.worktime || '9:30';
const $radio = $app.$manhour.querySelector(`.y-app-radio__input[name="y-app-manhour-time"][value="${ options.start }"]`);
if ($radio) {
$radio.click();
}
},
after: (options) => {
data.duration = options.duration;
$app.$manhour.$detail.innerHTML = VIEW_MANHOUR.render(data).trimHTML();
if (data.countdown < 1) {
data.countdown = countdownValue;
getManhour();
}
}
});
getManhour();
console.log(`[OK] 初始化 工时统计`);
}
$app.$nav = $app.$root.querySelector('#y-app-nav');
$app.$navs = $app.$nav.querySelectorAll('.y-app-nav__item');
$app.$main = $app.$root.querySelector('.y-app-main');
$app.$views = $app.$root.querySelectorAll('.y-app-view');
// 书签
$app.$bookmarks = $app.$root.querySelector('#y-app-bookmarks');
// 搜索器
$app.$search = $app.$root.querySelector('#y-app-search-form');
$app.$search.$keyword = $app.$root.querySelector('#y-app-search-keyword');
$app.$search.$keyword.value = this.query.get('keyword');
$app.$search.$result = $app.$root.querySelector('#y-app-search-result');
$app.$search.$size = $app.$root.querySelector('#y-app-search-size');
// 搜索
$app.$search.trim = (keyword) => {
keyword = (keyword || '').toString().trim();
keyword = keyword.replace(/^【(.*?)】/, '').split('https://')[0].trim();
return keyword;
};
$app.$search.search = (keyword, isUpdateInputValue) => {
keyword = $app.$search.trim(keyword);
// 是否更新搜索关键字
if (isUpdateInputValue) {
$app.$search.$keyword.value = keyword;
}
var query = {
// 搜索关键字
keyword,
// 每页显示条数
size: parseInt($app.$search.$size.value),
// 与我有关的
involved: state.involved
}
var $types = $app.$search.querySelectorAll('input[name="y-app-search-types"]:checked');
// console.log('$types:', $types);
var $fetchs = [];
$types.forEach((item) => {
((object) => {
if (VIEWS[object]) {
// console.log('=>>', VIEWS[object]);
$fetchs.push($fetch('https://api.yidayun.com/runtime/getList', VIEWS[object].params(query)).then((res) => {
VIEWS[object].records = res.data.records;
VIEWS[object].html = VIEWS[object].render(VIEWS[object], res.data.records);
return VIEWS[object];
}));
}
})(item.value);
});
Promise.all($fetchs).then((results) => {
var html_result = `${ results.map((item) => item.html).join('') }`.trim();
$app.$search.$result.innerHTML = html_result.trimHTML();
// console.log(results);
});
}
// 搜索按钮
$app.$search.addEventListener('submit', (event) => {
event.preventDefault();
const keyword = $app.$search.trim($app.$search.$keyword.value);
const query = {};
if (keyword) {
query.keyword = keyword;
}
this.search(keyword, true);
this.$router.push({ path: '/search', query });
return false;
});
// 页码切换
$app.$search.$size.addEventListener('change', (event) => {
// console.log('$app.$search.$size', $app.$search.$size.value);
this.state.pagesize = parseInt($app.$search.$size.value);
this.saveState();
});
// 抽屉: 打开
$app.openDrawer = (data) => {
data = { title: '', url: '', ...data };
if ($app.$drawer) {
$app.$root.removeChild($app.$drawer);
$app.$drawer = null;
}
$app.$drawer = document.createElement('div');
$app.$drawer.className = 'y-app-drawer';
$app.$drawer.tabIndex = 0;
$app.$drawer.innerHTML = `
<div class="y-app-drawer__backdrop"></div>
<div class="y-app-drawer__container">
<div class="y-app-drawer__head">
<button type="button" class="y-app-btn y-app-drawer__btn-close">${ ICONS.close }</button>
<a target="_blank" href="${ data.url || 'javascript:void(0)' }" class="y-app-btn y-app-btn-icon y-app-btn__copy y-app-action" data-action="prevent.copy" data-project="${ data.project }" data-name="${ data.name }" data-title="${ data.title }" title="复制链接">${ ICONS.copy }</a>
<h3 class="y-app-drawer__title">${ data.title }</h3>
</div>
<div class="y-app-drawer__main">
<iframe class="y-app-drawer__iframe" frameborder="0" scrolling="no" src="${ data.url }&sb=0"></div>
</div>
</div>
`.trim().trimHTML();
$app.$drawer.addEventListener('keydown', (event) => {
event.stopPropagation();
console.log('drawer.keydown', event);
if (event.keyCode === 27) {
$app.closeDrawer();
}
});
$app.$drawer.$backdrop = $app.$drawer.querySelector('.y-app-drawer__backdrop');
$app.$drawer.$backdrop.addEventListener('click', (event) => {
event.preventDefault();
// event.stopPropagation();
$app.closeDrawer();
});
$app.$drawer.$container = $app.$drawer.querySelector('.y-app-drawer__container');
$app.$drawer.$container.addEventListener('click', (event) => {
event.preventDefault();
// event.stopPropagation();
});
$app.$drawer.$btnClose = $app.$drawer.querySelector('.y-app-drawer__btn-close');
$app.$drawer.$btnClose.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
$app.closeDrawer();
});
// get current userTabs
this.userTabs = window.sessionStorage.getItem(USER_INFO.get('keyTabs'));
try {
this.userTabs = JSON.parse(this.userTabs);
if (Array.isArray(this.userTabs)) {
// backup to localStorage
window.localStorage.setItem(USER_INFO.get('keyTabs'), JSON.stringify(this.userTabs));
// remove tabs
window.sessionStorage.removeItem(USER_INFO.get('keyTabs'));
} else {
this.userTabs = null;
}
} catch (ex) {
console.error(ex);
}
$app.$root.appendChild($app.$drawer);
setTimeout(() => {
$app.$drawer.classList.add('active');
$app.$drawer.focus();
}, 10);
}
// 抽屉: 关闭
$app.closeDrawer = () => {
if (!$app.$drawer) {
return;
}
// restore tabs
if (this.userTabs && Array.isArray(this.userTabs)) {
let currentTabs = window.sessionStorage.getItem(USER_INFO.get('keyTabs'));
// merge tabs
try {
currentTabs = JSON.parse(currentTabs);
if (Array.isArray(this.userTabs) && Array.isArray(currentTabs)) {
currentTabs = this.userTabs.concat(currentTabs);
// console.log('before', currentTabs);
// 去除重复标签
let lastResId = null;
currentTabs = currentTabs.filter((item) => {
if (item.resId !== lastResId) {
lastResId = item.resId;
return true;
}
return false;
});
// console.log('after', currentTabs);
}
} catch (ex) {
console.warn(ex);
}
// save tabs
if (Array.isArray(currentTabs)) {
this.userTabs = JSON.stringify(currentTabs);
window.localStorage.setItem(USER_INFO.get('keyTabs'), this.userTabs);
window.sessionStorage.setItem(USER_INFO.get('keyTabs'), this.userTabs);
}
}
$app.$drawer.classList.remove('active');
setTimeout(() => {
$app.$root.removeChild($app.$drawer);
$app.$drawer = null;
}, 350);
}
// 事件处理器
$app.$root.addEventListener('click', (event) => {
event.stopPropagation();
const $el = event.target;
if ($el && $app.$root.contains($el) && $el.classList.contains('y-app-action')) {
let action = $el.dataset.action;
if (!action) {
return false;
}
// console.log('action:', action);
if (action.startsWith('prevent.')) {
event.preventDefault();
}
if (action.indexOf('.') > -1) {
action = action.split('.')[1] || '';
}
switch (action) {
case 'openDrawer':
this.getRecordByElement($el).then((data) => {
$app.openDrawer(data.record);
});
break;
case 'star':
this.getRecordByElement($el).then((data) => {
if (data.from === 'search') {
BOOKMARKS.add(data.record.bookmark, ({ type }) => {
$el.innerHTML = ICONS[type === 'add' ? 'starred' : 'star'];
$el.title = type === 'add' ? '取消收藏' : '收藏';
$app.$bookmarks.update();
});
} else {
$app.$bookmarks.unstar(data.record.resId);
BOOKMARKS.remove(data.record, ({ type }) => {
$app.$bookmarks.update();
});
}
});
break;
case 'copy':
let data = { project: '', name: '', url: '', title: '' };
data.project = $el.dataset.project || '';
data.name = $el.dataset.name || '';
data.url = $el.dataset.url || $el.getAttribute('href') || '';
data.title = $el.dataset.title || (data.project ? `【${ data.project }】` : '') + data.name;
doCopy(`${ data.title } \n${ data.url }`).then(() => {
$el.innerHTML = ICONS.check;
setTimeout(() => {
$el.innerHTML = ICONS.copy;
}, 3000);
});
break;
case 'setWorkTime':
$el.$input = $el.querySelector('input[type="radio"]');
if ($el.$input) {
setTimeout(() => {
// console.log('setWorkTime', $el.$input.value);
this.state.worktime = $el.$input.value;
this.saveState();
}, 50);
}
break;
case 'checkbox':
$el.$input = $el.querySelector('input[type="checkbox"]');
if ($el.$input) {
// $el.$input.checked = $el.$input.checked;
setTimeout(() => {
console.log('$input', $el.$input.checked, $el.$input.value);
// 与我有关的 checkbox
if ($el.$input.value === 'involved') {
this.state.involved = $el.$input.checked;
} else {
this.state.types[$el.$input.value] = $el.$input.checked;
}
this.saveState();
}, 50);
}
break;
case 'switchView':
// console.log('r:', this.$router);
this.$router.push('/'+ $el.dataset.view);
break;
case 'load':
// console.log('r:', this.$router);
$app.$search.search()
break;
case 'copyManhours':
var $table = $app.$manhourResult.querySelector('table');
if (!$table) {
return;
}
$el.setAttribute('disabled', 'disabled');
copyElementContents($table).then(() => {
$el.innerText = '复制成功';
setTimeout(() => {
$el.innerText = '复制';
$el.removeAttribute('disabled');
}, 3000);
}).catch(() => {
$el.removeAttribute('disabled');
});
break;
case 'viewManhours':
if (this.#VIEW_MANHOUR_MEMBER.loading) {
return false;
}
var fd = $app.getManhoursFormData();
fd.start = new Date(`${ fd.year }/${ fd.month }/1 00:00:00`);
fd.end = new Date(`${ fd.year }/${ fd.month }/1 00:00:00`);
fd.end.setMonth(fd.end.getMonth() + 1);
fd.end.setDate(0);
fd.monthRange = [];
fd.monthRange.push(fd.start.getTime());
fd.monthRange.push(fd.end.getTime());
// console.log('fd:', fd);
// console.log('fd:', fd.start.toLocaleString(), ' -> ', fd.end.toLocaleString());
this.#VIEW_MANHOUR_MEMBER.loading = true;
$el.innerText = '加载中...';
$app.$manhourResult.innerHTML = `<h2 class="y-app-h2 y-app-italic"> 加载中... </h2>`;
this.#VIEW_MANHOUR_MEMBER.getManhours(fd.groupby, fd.monthRange).then((data) => {
$app.$manhourResult.innerHTML = this.#VIEW_MANHOUR_MEMBER.render(data);
}).catch((ex) => {
console.error(`[ERROR]`, ex);
$app.$manhourResult.innerHTML = `<h2 class="y-app-h2 y-app-italic">出错了, 请重试</h2>`
}).finally(() => {
$el.innerText = '查看';
this.#VIEW_MANHOUR_MEMBER.loading = false;
});
break;
default:
break;
}
}
});
$app.$bookmarks.unstar = (resId) => {
let $el = $app.$search.$result.querySelector(`.y-app-btn-icon[data-id="${ resId }"]`);
if ($el) {
$el.innerHTML = ICONS.star;
$el.title = '收藏';
}
}
$app.$bookmarks.update = () => {
const html = VIEWS.map((view) => {
return view.render(view, BOOKMARKS.get(view.object), { from: 'bookmark' });
}).join('');
$app.$bookmarks.innerHTML = html.trimHTML() || `<h2 class="y-app-h2"> ~(^_^)~ </h2>`;
}
$app.$bookmarks.update();
// favicon
$app.$fav = document.createElement('link');
$app.$fav.setAttribute('rel', 'icon');
$app.$fav.setAttribute('href', 'https://www.yidayun.com/images/favicon.png');
document.head.appendChild($app.$fav);
// mounted
$app.$shadow.appendChild($app.$root);
this.$app = $app;
this.trigger('beforeMount');
console.log(`[OK] 初始化 易用云 主界面`);
if (typeof callback === 'function') {
callback();
}
}
get search() {
return (keyword, isUpdateInputValue) => {
const { $app } = this;
if (!$app) {
return;
}
keyword = (keyword || '').toString().trim();
$app.$search.$keyword.value = keyword;
$app.$search.search(keyword, isUpdateInputValue);
}
}
get createHomeView() {
return (path) => {
const { $app } = this;
if (!$app) {
return;
}
path = `https://web.yidayun.com${ path || '/home' }`;
if (path && path === this.homeViewURL) {
console.log('URL 无变化');
return;
}
if ($app.$homeView) {
$app.$main.removeChild($app.$homeView);
}
this.homeViewURL = path;
$app.$homeView = document.createElement('div');
$app.$homeView.className = 'y-app-view y-app-view__home active';
$app.$homeView.dataset.view = 'home';
$app.$homeView.innerHTML = `
<div class="y-app-view__container">
<iframe class="y-app-view__iframe" src="${ path }" frameborder="0" scrolling="no"></iframe>
</div>
`.trimHTML();
$app.$main.appendChild($app.$homeView);
}
}
get switchView() {
return (view) => {
const { $app } = this;
if (!$app) {
return;
}
// $app.$navs = $app.$nav.querySelectorAll('.y-app-nav__item');
$app.$views = $app.$root.querySelectorAll('.y-app-view');
let $nav, $view;
$app.$navs.forEach((item) => {
item.classList.remove('active');
(($el) => {
if ($el.dataset.view === view) {
$nav = $el;
}
})(item);
});
$app.$views.forEach((item) => {
item.classList.remove('active');
(($el) => {
if ($el.dataset.view === view) {
$view = $el;
}
})(item);
});
if ($nav) {
$nav.classList.add('active');
}
if ($view) {
$view.classList.add('active');
}
}
}
// 标签页备份器
tabsBackuper() {
let tab = null;
let tabs = null;
const keyTab = USER_INFO.get('keyTab');
const keyTabs = USER_INFO.get('keyTabs');
// restore to sessionStorage
tab = window.localStorage.getItem('y-app-CUR_TAB');
tabs = window.localStorage.getItem('y-app-TABS');
if (tabs) {
window.sessionStorage.setItem(keyTab, tab);
window.sessionStorage.setItem(keyTabs, tabs);
}
// loop backup to localStorage
this.timer_backuper = setInterval(() => {
tab = window.sessionStorage.getItem(keyTab);
tabs = window.sessionStorage.getItem(keyTabs);
if (tabs) {
window.localStorage.setItem('y-app-CUR_TAB', tab);
window.localStorage.setItem('y-app-TABS', tabs);
}
}, 1500);
console.log(`[OK] 初始化 标签页备份器`);
}
// 屏蔽日志监控&垃圾
block_something_bad() {
var block_it = function() {
// baidu
if (window._agl && window._agl.stop) {
// window._agl.push = () => {};
// window._agl.ext._v = '9.9.6';
// window._agl.ext._s = '996';
// window._agl.ext.xAngeliaLogid = '996';
window._agl.stop();
}
// aliyun
if (window.__bl && window.__bl.removeHook) {
window.__bl.removeHook();
if (window.__bl._conf) {
window.__bl._conf.debug = true;
window.__bl._conf.enableSPA = false;
window.__bl._conf.environment = 'dev';
window.__bl._conf.imgUrl = '';
window.__bl._conf.ignoreUrlPath = '/';
}
}
}
setInterval(() => {
block_it();
}, 500);
block_it();
var clean_it = function() {
window.sessionStorage.removeItem('_bl_sid');
for (var key in window.localStorage) {
if (key && key.startsWith('fclog_')) {
window.localStorage.removeItem(key);
}
}
}
setInterval(() => {
clean_it();
}, 1000 * 3);
clean_it();
console.log(`[OK] 初始化 屏蔽垃圾监控请求`);
}
get use() {
const that = this;
return function(plugin) {
const args = [ ...arguments ];
args[0] = that;
if (plugin && typeof plugin.install === 'function') {
// console.log('..', args);
plugin.install.apply(that, args);
}
}
}
// 事件
#EVENTS = {};
get on() {
const that = this
const EVENTS = this.#EVENTS;
return (eventType, handler) => {
if (typeof eventType !== 'string' || typeof handler !== 'function') {
return;
}
EVENTS[eventType] = EVENTS[eventType] || [];
EVENTS[eventType].push(handler);
}
}
get off() {
const that = this
const EVENTS = this.#EVENTS;
return (eventType, handler) => {
if (typeof eventType !== 'string' || !Array.isArray(EVENTS[eventType])) {
return;
}
if (typeof handler === 'function') {
EVENTS[eventType] = EVENTS[eventType].map((item) => {
return item !== handler;
});
return;
}
if (handler === undefined) {
delete EVENTS[eventType];
}
}
}
get trigger() {
const that = this
const EVENTS = this.#EVENTS;
return function(eventType) {
const args = [ ...arguments ];
args[0] = { type: eventType, target: that };
// args.splice(0, 1);
if (typeof eventType !== 'string' || !Array.isArray(EVENTS[eventType])) {
return;
}
EVENTS[eventType].forEach((item) => {
((handler) => {
// console.log('trigger.handler', handler, args);
handler.apply(that, args);
})(item);
});
}
}
init() {
if (this.inited) {
return console.warn(`[WARN] 已经初始化过了`);
}
this.block_something_bad();
this.injectGlobalStyle();
this.getUserInfo().then((data) => {
// 非iframe 才初始化 易用云 主视图
if (window.top === window) {
// 回到极速版
const pathname = window.location.pathname;
if (pathname.endsWith('/home') || pathname.endsWith('/share/form') || pathname.endsWith('/designer/form') || pathname.endsWith('/app-market')) {
window.location.href = window.location.href.replace(/^https\:\/\/web\.yidayun\.com\//, '/home.html#/');
return false;
}
this.initMainView(() => {
this.tabsBackuper();
this.trigger('mounted');
// 定时更新工时
this.$app.updateManhour();
});
}
}).catch((err) => {
console.error(err);
if (window.location.pathname.startsWith('/login')) {
return false;
}
const $message = document.createElement('div');
$message.innerHTML = `
<center><h1>${ err.errorMessage }</h1></center><hr><center>错误码 (${ err.errorCode })</center>
${ err.errorCode === 403 ? `<center><h3>会话失效,<a rel="opener" style="color:#247fff;" href="https://web.yidayun.com/login?callback=${ encodeURIComponent(window.location.href) }" title="会话失效,需重新登录(不可用)">重新登录(不可用)</a></h3></center>` : '' }
`.trimHTML();
document.body.insertBefore($message, document.body.childNodes[0]);
});
this.inited = true;
}
}
class ROUTER {
#OPTIONS = {
base: '',
history: false, // hash
route: null,
routes: [],
routesMap: {},
beforeEach: (to, from, next) => {
if (typeof next === 'function') {
next();
}
},
afterEach: (to, from) => {}
};
#POPSTATE = [];
pushState = ((fn) => {
const OPTIONS = this.#OPTIONS;
return function(state, title, url) {
if (typeof state === 'object') {
state = JSON.parse(JSON.stringify(state));
arguments[0] = state;
}
if (OPTIONS.history) {
fn.apply(window.history, arguments);
} else {
OPTIONS.isForward = true;
window.location.hash = url;
setTimeout(() => {
delete OPTIONS.isForward;
}, 100)
}
}
})(window.history.__proto__.pushState);
replaceState = ((fn) => {
const OPTIONS = this.#OPTIONS;
return function(state, title, url) {
if (typeof state === 'object') {
state = JSON.parse(JSON.stringify(state));
arguments[0] = state;
}
if (OPTIONS.history) {
fn.apply(window.history, arguments);
} else {
OPTIONS.isForward = true;
window.location.replace('#'+ url);
setTimeout(() => {
delete OPTIONS.isForward;
}, 100)
}
}
})(window.history.__proto__.replaceState);
constructor(options) {
options = { ...options };
if (options.base) {
this.#OPTIONS.base = options.base;
}
if (options.history) {
this.#OPTIONS.history = options.history;
}
if (options.routes) {
this.add(options.routes);
}
if (options.beforeEach) {
this.beforeEach(options.beforeEach);
}
if (options.afterEach) {
this.afterEach(options.afterEach);
}
console.log(`[OK] 初始化 路由 ...`);
}
get path() {
const OPTIONS = this.#OPTIONS;
let path = '';
if (OPTIONS.history) {
path = window.location.pathname;
} else {
path = window.location.hash.substr(1).split('?')[0];
}
path = path.substr(OPTIONS.base.length);
return path;
}
get query() {
const OPTIONS = this.#OPTIONS;
let query = '';
if (OPTIONS.history) {
query = window.location.search.substr(1);
} else {
query = window.location.hash.split('?')[1] || '';
}
query = new URLSearchParams(query);
const qs = query.toString();
query = Object.fromEntries(query);
Object.defineProperty(query, 'toString', {
get() {
return () => {
return qs;
}
}
});
return query;
}
dispatch() {
const OPTIONS = this.#OPTIONS;
const { beforeEach, afterEach } = OPTIONS;
const onChange = (event) => {
// console.log('onChange', event);
if (event.type === 'hashchange' && OPTIONS.isForward) {
// console.error('[isForward]', OPTIONS.isForward);
delete OPTIONS.isForward;
return;
}
const form = OPTIONS.route;
const to = OPTIONS.routesMap[this.path];
// console.log('onChange.meta', to.meta);
// console.log('change.to', to, form)
if (to && to.path && to.handler) {
to.meta ={ ...to.meta };
to.query = this.query;
to.qs = to.query.toString();
if (form) {
form.meta = { ...form.meta };
}
const next = () => {
this.#OPTIONS.route = to;
to.handler();
if (typeof afterEach === 'function') {
afterEach(form, to);
}
}
if (typeof beforeEach === 'function') {
beforeEach(to, form, next);
} else {
next();
}
}
}
// bind event
if (OPTIONS.history) {
window.addEventListener('popstate', onChange);
} else {
window.addEventListener('hashchange', onChange);
}
// start
const { routes, routesMap } = OPTIONS;
const to = { meta: {}, query: this.query };
let currentRoute = null;
if (routesMap[this.path]) {
currentRoute = routesMap[this.path];
} else {
if (routes.length) {
currentRoute = routes[0];
}
}
if (currentRoute) {
to.path = currentRoute.path;
to.meta = currentRoute.meta || to.meta;
}
this.push(to);
console.log(`[OK] 初始化 路由 开始监听`);
}
get beforeEach() {
return (fn) => {
if (typeof fn === 'function') {
this.#OPTIONS.beforeEach = fn;
}
}
}
get afterEach() {
return (fn) => {
if (typeof fn === 'function') {
this.#OPTIONS.afterEach = fn;
}
}
}
get options() {
return this.#OPTIONS;
}
get add() {
return (route, handler) => {
if (Array.isArray(route)) {
route.forEach((item) => {
((_route) => {
this.add(_route, handler);
})(item);
});
return;
}
if (typeof route === 'string' && route.length) {
route = { path: route };
}
if (typeof route === 'object' && typeof handler === 'function') {
route.handler = handler || route.handler;
}
if (typeof route.path === 'string' && route.path.length && typeof route.handler === 'function') {
// route.path = this.#OPTIONS.base + route.path;
route.meta = { ...route.meta };
if (this.#OPTIONS.routesMap[route.path]) {
return console.warn(`[WARN] route has existed`);
}
this.#OPTIONS.routesMap[route.path] = route;
this.#OPTIONS.routes.push(route);
}
}
}
get $route() {
return this.#OPTIONS.route;
}
get $routes() {
return this.#OPTIONS.routes;
}
get $router() {
return this;
}
get parse() {
return (route) => {
if (typeof route === 'string' && route.length) {
route = { path: route };
}
if (typeof route === 'object' && typeof route.path === 'string' && route.path.length) {
route.path = route.path.split('?');
route.qs = route.path[1] || '';
route.path = route.path[0];
route.qs = new URLSearchParams(route.qs).toString();
if (route.query instanceof URLSearchParams) {
route.query = route.query.toString();
} else {
route.query = new URLSearchParams(route.query).toString();
}
route.qs+= (route.qs.length && route.query.length ? '&' : '') + route.query;
route.query = Object.fromEntries(new URLSearchParams(route.qs));
// route.qs = (route.qs.length ? '?' : '') + route.qs;
return route;
}
}
}
get push() {
return (to) => {
to = this.parse(to);
if (!to || !to.path) {
return console.log(`[ERROR] route path error`);
}
const { base, route, routesMap, beforeEach, afterEach } = this.#OPTIONS;
if (route && route.path === to.path && route.qs === to.qs) {
return;
}
const currentRoute = routesMap[to.path];
if (currentRoute && currentRoute.path && currentRoute.handler) {
const form = route;
to.meta = { ...currentRoute.meta };
// console.log('push.meta', to.meta);
to.handler = currentRoute.handler;
if (form) {
form.meta ={ ...form.meta };
}
const next = () => {
this.pushState(to, form, base + to.path + (to.qs ? '?' : '') + to.qs);
this.#OPTIONS.route = to;
to.handler();
if (typeof afterEach === 'function') {
afterEach(form, to);
}
}
if (typeof beforeEach === 'function') {
beforeEach(to, form, next);
} else {
next();
}
}
}
}
get replace() {
return (to) => {
to = this.parse(to);
if (!to || !to.path) {
return console.log(`[ERROR] route path error`);
}
const { base, route, routesMap, beforeEach, afterEach } = this.#OPTIONS;
if (route && route.path === to.path && route.qs === to.qs) {
return;
}
const currentRoute = routesMap[to.path];
if (currentRoute && currentRoute.path && currentRoute.handler) {
const form = route;
to.meta = { ...currentRoute.meta };
// console.log('replace.meta', to.meta);
to.handler = currentRoute.handler;
if (form) {
form.meta ={ ...form.meta };
}
const next = () => {
this.replaceState(to, form, base + to.path + (to.qs ? '?' : '') + to.qs);
this.#OPTIONS.route = to;
to.handler();
if (typeof afterEach === 'function') {
afterEach(form, to);
}
}
if (typeof beforeEach === 'function') {
beforeEach(to, form, next);
} else {
next();
}
}
}
}
get back() {
return () => {
window.history.back();
}
}
get reload() {
return () => {
window.location.reload();
}
}
get install() {
return (app) => {
// console.log('install.app', app);
if (app && typeof app.use === 'function') {
Object.defineProperties(app, {
$route: {
get: () => {
return this.$route
}
},
$routes: {
get: () => {
return this.$routes
}
},
$router: {
get: () => {
return this.$router
}
}
});
app.on('beforeMount', () => {
this.dispatch();
});
}
}
}
}
// html 时才注入
if (document.contentType === 'text/html') {
const router = new ROUTER({
base: '',
routes: [
{
path: '/home',
meta: {
title: ''
},
handler() {
if (this.query.view) {
$Y.createHomeView('/home?view='+ encodeURIComponent(this.query.view) );
console.log('view:', this.query.view);
} else {
$Y.createHomeView();
}
$Y.switchView('home');
}
},
{
path: '/share/form',
meta: {
title: ''
},
handler() {
if (this.query.number) {
$Y.createHomeView('/share/form?number='+ encodeURIComponent(this.query.number) );
}
$Y.switchView('home');
}
},
{
path: '/designer/form',
meta: {
title: ''
},
handler() {
if (this.query.object) {
$Y.createHomeView('/designer/form?object='+ encodeURIComponent(this.query.object) );
}
$Y.switchView('home');
}
},
{
path: '/app-market',
meta: {
title: ''
},
handler() {
$Y.createHomeView('/app-market?'+ this.qs);
$Y.switchView('home');
}
},
{
path: '/bookmarks',
meta: {
title: '收藏夹'
},
handler() {
$Y.switchView('bookmarks');
}
},
{
path: '/search',
meta: {
title: '需求/任务/工时'
},
handler() {
$Y.switchView('search');
}
},
{
path: '/manhour',
meta: {
title: '工时'
},
handler() {
$Y.switchView('manhour');
}
},
]
});
router.beforeEach((to, from, next) => {
// console.log('beforeEach:', { to, from, next });
// console.log('beforeEach.meta:', to.meta);
document.title = (to.meta.title ? to.meta.title +' - ' : '') + $Y.name;
next();
});
/*router.afterEach((to, from) => {
console.log('afterEach:', { to, from });
});*/
const $Y = new Y();
if (window.top === window) {
$Y.use(router);
$Y.on('mounted', (event) => {
// const app = event.target;
if ($Y.path.startsWith('/home?view=')) {
window.location.replace('/home.html#/home'+ window.location.search);
return;
}
if ($Y.path.startsWith('/share/form?number=')) {
window.location.replace('/home.html#/share/form'+ window.location.search);
return;
}
// event.target
// console.log('>>>s', this, $Y.path);
const keyword = $Y.$route.query.keyword;
if (keyword) {
$Y.search(keyword, true);
}
});
}
window.$Y = $Y;
}
})();