// ==UserScript==
// @name 易用云
// @namespace 让易搭云更易用!好用到爆炸!(云生集团-研发专用版)
// @version 0.0.1
// @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://greasyfork.org/zh-CN/scripts/480426
// @license MIT
// @description 2023/11/16 12:37:58
// ==/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 renderLinkHref = (data) => {
const view = {
type: 'windowAction',
target: 'tab',
object: data.object,
name: data.title || (data.project + data.name),
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',
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;
// 收藏夹
BOOKMARKS = null;
// 参与人选择器
PLAYERS = 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) {
return {
"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": {}
};
},
},
{
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) {
return {
"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": {}
};
},
},
{
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) {
return {
"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": {}
};
},
},
{
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) {
return {
"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": {}
};
},
},
{
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.updateTime }</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) {
return {
"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": {}
};
},
}
];
// 工时视图
#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 }小时</li>
<li title="昨日登记工时">昨日:${ data.yestoday }小时</li>
<li title="当前上班时长">${ data.duration }</li>
<li title="当前时间">时间:${ data.now }</li>
`.trim();
}
};
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();
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.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) }`;
now.setDate(now.getDate()-1);
data.yestodayTime = `${ now.getFullYear() }${ (now.getMonth()+1).toString().padStart(2, 0) }${ now.getDate().toString().padStart(2, 0) }`;
$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];
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>.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{
background-color: #fff !important;
}
/* 调整 右侧主题内容 内边距 */
#root .ant-pro-basicLayout>.ant-layout .eb-layout-container-section{
margin: 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;
}
}
}
}
.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: rgba(255,255,255,0.85);
background: #f9f9f9;
border-bottom: 1px solid #ccc;
*/
background-color: #fff;
box-shadow: 0 2px 10px rgba(0,0,0,.05);
}
.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;
}
.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-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: absolute;
top: 0;
right: 0;
height: var(--head-height);
overflow: hidden;
min-width: 228px;
padding: 4px 10px 10px 10px;
background: rgba(255,255,255, 0.88);
}
.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);
}
.y-app-manhour__detail,
.y-app-manhour__detail>li{
list-style: none;
margin: 0;
padding: 0;
}
.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 = () => {
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 y-app-hidden-v">
<label class="y-app-checkbox class="y-app-action" data-action="checkbox">
<input type="checkbox" class="y-app-checkbox__input" value="involved">
<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 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="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>
<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>
</div>
</div>
<div class="y-app-view y-app-view__manhour" data-view="manhour">
<h2 class="y-app-h2 y-app-italic"> MANHOUR </h2>
</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.$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;
let count = 0; // 计数
let resetCount = 29; // 重置计数
const data = {
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) => {
count++;
// console.log('counter:', counter);
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 (count && count % resetCount === 0) {
count = 0;
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.switchView = (view) => {
// console.log('view:', 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');
}
let search = new URLSearchParams();
if (view === 'search') {
search = this.query;
}
search.set('v', view);
// history.pushState(null, null, '?'+ search.toString());
}
// 绑定导航切换事件
$app.$navs.forEach((item) => {
(($nav) => {
$nav.addEventListener('click', (event) => {
event.preventDefault();
event.stopPropagation();
$app.switchView(event.target.dataset.view);
return false;
});
})(item);
});*/
// 书签
$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)
}
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();
});
$app.$root.appendChild($app.$drawer);
window.sessionStorage.removeItem(USER_INFO.get('keyTabs'));
setTimeout(() => {
$app.$drawer.classList.add('active');
$app.$drawer.focus();
}, 10);
}
// 抽屉: 关闭
$app.closeDrawer = () => {
if (!$app.$drawer) {
return;
}
$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);
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;
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();
// mounted
$app.$shadow.appendChild($app.$root);
this.$app = $app;
this.trigger('beforeMount');
console.log(`[OK] 初始化 易用云 主界面`);
if (typeof callback === 'function') {
callback();
}
}
get search() {
return (keyword) => {
const { $app } = this;
if (!$app) {
return;
}
keyword = (keyword || '').toString().trim();
$app.$search.$keyword.value = keyword;
$app.$search.search(keyword);
}
}
get createHomeView() {
let url = '';
return (path) => {
const { $app } = this;
if (!$app) {
return;
}
path = `https://web.yidayun.com${ path || '/home' }`;
if ($app.$homeView && url !== path) {
$app.$main.removeChild($app.$homeView);
}
url = 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');
}
}
}
// 屏蔽日志监控&垃圾
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) {
this.initMainView(() => {
this.trigger('mounted');
// 定时更新工时
this.$app.updateManhour();
});
}
});
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() {
console.log('/home', location.href, this);
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() {
console.log('/share/form');
if (this.query.number) {
$Y.createHomeView('/share/form?number='+ encodeURIComponent(this.query.number) );
console.log('number:', this.query.number);
}
$Y.switchView('home');
}
},
{
path: '/bookmarks',
meta: {
title: '收藏夹'
},
handler() {
console.log('/bookmarks');
$Y.switchView('bookmarks');
}
},
{
path: '/search',
meta: {
title: '需求/任务/工时'
},
handler() {
console.log('/search');
// console.log('rrr', this);
$Y.switchView('search');
}
},
]
});
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;
}
})();