Commit 82cd849f by 胡锦波

1. init 非报错添加

parent d2a29820
......@@ -3269,8 +3269,7 @@
"base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
"dev": true
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="
},
"batch": {
"version": "0.6.1",
......@@ -4665,6 +4664,11 @@
"randomfill": "^1.0.3"
}
},
"crypto-js": {
"version": "4.1.1",
"resolved": "http://npm.job.qinqinxiaobao.com/crypto-js/-/crypto-js-4.1.1.tgz",
"integrity": "sha1-nkhbzwNSEEG9hYRHhrg/t2GXNs8="
},
"css-color-names": {
"version": "0.0.4",
"resolved": "https://registry.npmjs.org/css-color-names/-/css-color-names-0.0.4.tgz",
......@@ -9268,6 +9272,25 @@
"integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==",
"dev": true
},
"oidc-client": {
"version": "1.11.5",
"resolved": "http://npm.job.qinqinxiaobao.com/oidc-client/-/oidc-client-1.11.5.tgz",
"integrity": "sha1-Agqhk9aKPh+Hok/L9QBztzjekrs=",
"requires": {
"acorn": "^7.4.1",
"base64-js": "^1.5.1",
"core-js": "^3.8.3",
"crypto-js": "^4.0.0",
"serialize-javascript": "^4.0.0"
},
"dependencies": {
"acorn": {
"version": "7.4.1",
"resolved": "http://npm.job.qinqinxiaobao.com/acorn/-/acorn-7.4.1.tgz",
"integrity": "sha1-/q7SVZc9LndVW4PbwIhRpsY1IPo="
}
}
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
......@@ -10516,7 +10539,6 @@
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
"integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
"dev": true,
"requires": {
"safe-buffer": "^5.1.0"
}
......@@ -10973,8 +10995,7 @@
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
"dev": true
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
},
"safe-regex": {
"version": "1.1.0",
......@@ -11085,7 +11106,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz",
"integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==",
"dev": true,
"requires": {
"randombytes": "^2.1.0"
}
......
......@@ -12,6 +12,7 @@
"core-js": "^3.6.5",
"element-ui": "^2.15.7",
"lodash": "^4.17.21",
"oidc-client": "^1.11.5",
"uniplat-sdk": "^0.1.350-private",
"vue": "^2.6.11",
"vue-class-component": "^7.2.3",
......
/* eslint-disable */
/**
* 字符串转16进制
*/
function bin2hex(s: string) {
let i,
l,
o = "",
n;
s += "";
for (i = 0, l = s.length; i < l; i++) {
n = s.charCodeAt(i).toString(16)
o += n.length < 2 ? "0" + n : n;
}
return o;
}
function getCanvas() {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d") as CanvasRenderingContext2D;
ctx.fillStyle = "#f60";
ctx.fillRect(125, 1, 62, 20);
const b64 = canvas.toDataURL().replace("data:image/png;base64,", "");
const bin = atob(b64);
const crc = bin2hex(bin.slice(-16, -12));
return crc;
}
function getOsInfo() {
const userAgent = navigator.userAgent.toLowerCase();
let name = "Unknown";
let version = "Unknown";
if (userAgent.indexOf("win") > -1) {
name = "Windows";
if (userAgent.indexOf("windows nt 5.0") > -1) {
version = "Windows 2000";
} else if (
userAgent.indexOf("windows nt 5.1") > -1 ||
userAgent.indexOf("windows nt 5.2") > -1
) {
version = "Windows XP";
} else if (userAgent.indexOf("windows nt 6.0") > -1) {
version = "Windows Vista";
} else if (
userAgent.indexOf("windows nt 6.1") > -1 ||
userAgent.indexOf("windows 7") > -1
) {
version = "Windows 7";
} else if (
userAgent.indexOf("windows nt 6.2") > -1 ||
userAgent.indexOf("windows 8") > -1
) {
version = "Windows 8";
} else if (userAgent.indexOf("windows nt 6.3") > -1) {
version = "Windows 8.1";
} else if (
userAgent.indexOf("windows nt 6.2") > -1 ||
userAgent.indexOf("windows nt 10.0") > -1
) {
version = "Windows 10";
} else {
version = "Unknown";
}
} else if (userAgent.indexOf("iphone") > -1) {
name = "Iphone";
} else if (userAgent.indexOf("mac") > -1) {
name = "Mac";
} else if (
userAgent.indexOf("x11") > -1 ||
userAgent.indexOf("unix") > -1 ||
userAgent.indexOf("sunname") > -1 ||
userAgent.indexOf("bsd") > -1
) {
name = "Unix";
} else if (userAgent.indexOf("linux") > -1) {
if (userAgent.indexOf("android") > -1) {
name = "Android"
} else {
name = "Linux";
}
} else {
name = "Unknown";
}
return { version, name };
}
function regMatch(agent: string, reg: RegExp, defaultIndex = 1, defaultValue = '') {
const match = agent.match(reg);
if (match) {
return match[defaultIndex];
}
return defaultValue;
}
function getBrowerInfo() {
const Browser: any =
(function(window) {
const document = window.document,
navigator = window.navigator,
agent = navigator.userAgent.toLowerCase(),
//IE8+支持.返回浏览器渲染当前文档所用的模式
//IE6,IE7:undefined.IE8:8(兼容模式返回7).IE9:9(兼容模式返回7||8)
//IE10:10(兼容模式7||8||9)
IEMode = (<any>document).documentMode,
//chorme
chrome = (<any>window).chrome || false,
System = {
//user-agent
agent: agent,
//是否为IE
isIE: /trident/.test(agent),
//Gecko内核
isGecko: agent.indexOf("gecko") > 0 && agent.indexOf("like gecko") < 0,
//webkit内核
isWebkit: agent.indexOf("webkit") > 0,
//是否为标准模式
isStrict: document.compatMode === "CSS1Compat",
//是否支持subtitle
supportSubTitle: function() {
return "track" in document.createElement("track");
},
//是否支持scoped
supportScope: function() {
return "scoped" in document.createElement("style");
},
//获取IE的版本号
ieVersion: function() {
const rMsie = /(msie\s|trident.*rv:)([\w.]+)/;
const ma = window.navigator.userAgent.toLowerCase()
const match = rMsie.exec(ma);
if (match) {
return match[2];
}
return IEMode;
},
//Opera版本号
operaVersion: function() {
try {
if ((<any>window).opera) {
return regMatch(agent, /opera.([\d.]+)/);
} else if (agent.indexOf("opr") > 0) {
return regMatch(agent, /opr\/([\d.]+)/);
}
} catch (e) {
return 0;
}
}
};
try {
//浏览器类型(IE、Opera、Chrome、Safari、Firefox)
(<any>System).type = System.isIE
? "IE"
: (<any>window).opera || agent.indexOf("opr") > 0
? "Opera"
: agent.indexOf("chrome") > 0
? "Chrome"
: //safari也提供了专门的判定方式
(<any>window).openDatabase
? "Safari"
: agent.indexOf("firefox") > 0
? "Firefox"
: "unknow";
//版本号
(<any>System).version = ((<any>System).type === "IE") ? System.ieVersion() :
((<any>System).type === "Firefox") ? regMatch(agent, /firefox\/([\d.]+)/) :
((<any>System).type === "Chrome") ? regMatch(agent, /chrome\/([\d.]+)/) :
((<any>System).type === "Opera") ? System.operaVersion() :
((<any>System).type === "Safari") ? regMatch(agent, /version\/([\d.]+)/) :
"0";
//浏览器外壳
(<any>System).shell = function() {
if (agent.indexOf("edge") > 0) {
(<any>System).version = regMatch(agent, /edge\/([\d.]+)/) || (<any>System).version;
return "edge浏览器";
}
//遨游浏览器
if (agent.indexOf("maxthon") > 0) {
(<any>System).version = regMatch(agent, /maxthon\/([\d.]+)/) || (<any>System).version;
return "傲游浏览器";
}
//QQ浏览器
if (agent.indexOf("qqbrowser") > 0) {
(<any>System).version = regMatch(agent, /qqbrowser\/([\d.]+)/) || (<any>System).version;
return "QQ浏览器";
}
//搜狗浏览器
if (agent.indexOf("se 2.x") > 0) {
return '搜狗浏览器';
}
//Chrome:也可以使用window.chrome && window.chrome.webstore判断
if (chrome && (<any>System).type !== "Opera") {
const external = window.external,
clientInfo = window.clientInformation,
//客户端语言:zh-cn,zh.360下面会返回undefined
clientLanguage = clientInfo.languages;
//猎豹浏览器:或者agent.indexOf("lbbrowser")>0
if (external && 'LiebaoGetVersion' in external) {
return '猎豹浏览器';
}
//百度浏览器
if (agent.indexOf("bidubrowser") > 0) {
(<any>System).version =
regMatch(agent, /bidubrowser\/([\d.]+)/) ||
regMatch(agent, /chrome\/([\d.]+)/);
return "百度浏览器";
}
//360极速浏览器和360安全浏览器
if (System.supportSubTitle() && typeof clientLanguage === "undefined") {
//object.key()返回一个数组.包含可枚举属性和方法名称
const storeKeyLen = Object.keys(chrome.webstore).length;
return storeKeyLen > 1 ? '360极速浏览器' : '360安全浏览器';
}
return "Chrome";
}
return (<any>System).type;
};
//浏览器名称(如果是壳浏览器,则返回壳名称)
(<any>System).name = (<any>System).shell();
} catch (e) {
}
return { client: System };
})(window);
if (Browser.client.name == undefined || Browser.client.name == "") {
Browser.client.name = "Unknown";
Browser.client.version = "Unknown";
} else if (Browser.client.version == undefined) {
Browser.client.version = "Unknown";
}
return Browser;
}
let parameters2String = "";
function getParameters(): string {
if (!parameters2String) {
const browerInfo = getBrowerInfo()
const parameters = <{ [key: string]: string }>{
v: "1.0.0",
did: getCanvas(),
dh: getOsInfo().version,
db: browerInfo.client.name,
dv: browerInfo.client.version
}
parameters2String = Object.entries(parameters).map(([k, v]) => `${k}=${v}`).join('&');
}
return `${parameters2String}&t=${new Date().valueOf()}`;
}
/**
* 用于获取用户系统信息和环境配置
*/
export function getAuditorInfo() {
return getParameters();
}
import { UserManagerSettings } from "oidc-client";
import { Envir, Environment } from "./env";
export interface ApiConfig {
clientId: string;
clientSecret: string;
host?: string;
scope: string;
/**
* 组织管理后台地址
*/
manager: string;
passport: string;
chatApi: string;
chatWebSocket: string;
salaryUpload: string;
root: string;
rootDomain: string;
fileDownload: string;
accountCenter: string;
sdk: string;
enterpriseUrl: string;
}
export interface Config {
oidc: UserManagerSettings;
api: ApiConfig;
}
const pro = Envir.isPro();
const envir = Envir.getEnvir();
const clientId = "teamix-team-manager-web-pc";
const hostOptions = new Map<Environment, string>([
[Environment.Dev, window.location.origin],
[Environment.Pro, "https://account.hrs100.com"],
[Environment.Test, "http://106.120.107.150:7772"],
[Environment.Stage, "https://pre-account.hrs100.com"]
]);
const rootDomainOptions = new Map<Environment, string>([
[Environment.Dev, window.location.origin],
[Environment.Pro, "https://{0}.hrs100.com"],
[Environment.Test, "http://a.web.test.hrs100.cn"],
[Environment.Stage, "https://pre-{0}.hrs100.com"]
]);
const host = hostOptions.get(envir);
const rootDomain = rootDomainOptions.get(envir);
const api = {
clientId,
clientSecret: pro ? "qqxb#teammix#2019" : "123456",
scope: "api.workapps.open",
root: host
} as ApiConfig;
const oidc = {
client_id: clientId,
redirect_uri: `${host}/oidc-redirect`,
response_type: "id_token token",
scope:
"workapps.client api.workapps.user api.workapps.org api.workapps.open openid",
silent_redirect_uri: `${host}/oidc-silent-redirect`,
post_logout_redirect_uri: host
} as UserManagerSettings;
function getValue(map: Map<Environment, string>) {
return map.get(envir) || map.get(Environment.Dev);
}
const authorityOptions = new Map<Environment, string>([
[Environment.Dev, "http://106.120.107.150:5000"],
[Environment.Pro, "https://passport.teammix.com"],
[Environment.Stage, "https://pre-passport.teammix.com"]
]);
Object.assign(oidc, { authority: getValue(authorityOptions) });
Object.assign(api, { rootDomain });
export default { oidc, api };
import Axios, { AxiosRequestConfig } from "axios";
import qs from 'qs';
import { getAuditorInfo } from './auditor';
import config from './config';
import { Envir } from './env';
import { TokenManager } from './token';
interface TeamMixError {
msg?: string;
error?: string;
}
export interface TeamMixResponse extends TeamMixError {
code?: number;
rescode?: number;
errcode: number;
errmsg: string;
data: any;
status?: boolean;
message?: string;
}
export default abstract class BaseService {
private static vue: Vue | null = null;
protected authKey = 'PassportToken';
protected readonly teammixAuthKey = 'Authorization';
// private readonly enterpriseToken4Id = 'enterpriseToken4Id';
private readonly enterpriseList = 'enterpriseList';
private auditor = "";
private jsonAsContentType = false;
protected readonly positiveValue = 1;
protected readonly negativeValue = 0;
private onceAppId: string | number = 0;
protected constructor() {
if (!this.isDevEnvironment()) {
Axios.defaults.baseURL = config.api.host;
} else {
Axios.defaults.baseURL = "";
}
Axios.defaults.timeout = 30 * 1000;
Axios.interceptors.response.use(r => r,
r => {
if (r) {
const e = r;
if (e && e.message && _.isString(e.message)) {
if (_.includes(e.message, 'timeout')) {
e.message = `服务器繁忙,请稍候重试`;
}
if (/request failed/i.test(e.message)) {
e.message = '系统出现异常,请稍候重试';
} else if (/401/i.test(e.message)) {
e.message = '您的授权信息已过期,请尝试退出登录重试';
} else if (/403/i.test(e.message)) {
e.message = '您没有权限访问此页面';
}
}
}
return Promise.reject(r);
});
this.auditor = getAuditorInfo();
}
public static registerRootVueInstance(vue: Vue) {
BaseService.vue = vue;
}
public static getRootVueInstance() {
return BaseService.vue;
}
protected buildUrlWithAuditor(url: string) {
if (this.auditor) {
if (url.indexOf("?") > -1) {
return `${url}&${this.auditor}`;
}
url = `${url}?${this.auditor}`;
}
return url;
}
private hasError(data: TeamMixResponse) {
return data.rescode || data.errcode || data.status === false || data.code;
}
/**
* 部分请求需要使用json作为content-type,在子类的构造函数中调用此函数即可
*/
protected useApplicationJsonAsContentType() {
this.jsonAsContentType = true;
}
private invokeGet(url: string, config?: AxiosRequestConfig) {
return new Promise((resolve, reject) => {
Axios.get(this.buildUrlWithAuditor(url), config).then(r => {
if (r.data) {
const data = r.data as TeamMixResponse;
if (this.hasError(data)) {
reject(data);
return;
}
if (data) {
resolve(data.data ?? data);
return;
}
reject(data);
} else {
reject(r);
}
}).catch((e) => {
reject(e);
});
});
}
protected invokePost(url: string, body: any, config?: AxiosRequestConfig) {
return new Promise((resolve, reject) => {
const postData = this.jsonAsContentType ? body : (typeof body === 'string' ? body : qs.stringify(body || {}));
Axios.post(this.buildUrlWithAuditor(url), postData, config).then(r => {
if (r.data) {
const data = r.data as TeamMixResponse;
if (this.hasError(data)) {
reject(data);
return;
}
if (data) {
resolve(data.data);
return;
}
reject(data);
} else {
reject(r);
}
}).catch(e => {
reject(e);
});
});
}
protected invokeDelete(url: string, body: any, config?: AxiosRequestConfig) {
return new Promise((resolve, reject) => {
const postData = this.jsonAsContentType ? body : (typeof body === 'string' ? body : qs.stringify(body || {}));
config = config || {};
Object.assign(config, { data: postData });
Axios.delete(this.buildUrlWithAuditor(url), config).then(r => {
if (r.data) {
const data = r.data as TeamMixResponse;
if (this.hasError(data)) {
reject(data);
return;
}
if (data) {
resolve(data.data);
return;
}
reject(data);
} else {
reject(r);
}
}).catch(e => {
reject(e);
});
});
}
protected buildRequestConfig(config?: AxiosRequestConfig) {
config = config || {};
config.headers = config.headers || {};
return config;
}
protected async buildUserToken2Config(config?: AxiosRequestConfig) {
config = this.buildRequestConfig(config);
if (config.headers) {
config.headers[this.authKey] = await TokenManager.getUserTokenString();
}
return config;
}
public setEnterprise4Cache(data: any) {
localStorage.setItem('currentEnterPriseList', JSON.stringify(data));
}
public getEnterprise4Cache() {
const item = localStorage.getItem('currentEnterPriseList');
return (item && JSON.parse(item)) || null;
}
public getEnterpriseToken4RegisterId() {
const currentRegisterId = localStorage.getItem("currentTemmixCompany") || "";
const cache = this.getEnterprise4Cache();
if (cache && cache[currentRegisterId]) {
return cache[currentRegisterId].appRegisterId;
}
return '';
}
public getEnterpriseToken4Id() {
const currentRegisterId = localStorage.getItem("currentTemmixCompany") || "";
const cache = this.getEnterprise4Cache();
if (cache && cache[currentRegisterId]) {
return cache[currentRegisterId].id;
}
return '';
}
protected useOnceApp(app: string | number) {
this.onceAppId = app;
}
protected async buildAppToken2Config(config?: AxiosRequestConfig) {
config = this.buildRequestConfig(config);
if (config.headers) {
config.headers[this.authKey] = await TokenManager.getAppToken(this.onceAppId || this.getEnterpriseToken4RegisterId());
}
this.onceAppId = 0;
return this.checkContentType(config);
}
private checkContentType(config: AxiosRequestConfig) {
if (this.jsonAsContentType) {
if (config.headers) {
config.headers['Content-Type'] = 'application/json';
}
}
return config;
}
protected async get<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
return this.invokeGet(url, config) as Promise<T>;
}
protected async post<T>(url: string, data: any, config?: AxiosRequestConfig): Promise<T> {
return this.invokePost(url, data, config) as Promise<T>;
}
protected async getWithUserToken<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
config = await this.buildUserToken2Config(config);
return this.invokeGet(url, config) as Promise<T>;
}
protected async getWithAppToken<T>(url: string, config?: AxiosRequestConfig): Promise<T> {
config = await this.buildAppToken2Config(config);
return this.invokeGet(url, config) as Promise<T>;
}
protected async postWithUserToken<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
config = await this.buildUserToken2Config(config);
return this.invokePost(url, data, config) as Promise<T>;
}
protected async postWithAppToken<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
config = await this.buildAppToken2Config(config);
return this.invokePost(url, data, config) as Promise<T>;
}
protected async deleteWithAppToken<T>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T> {
config = await this.buildAppToken2Config(config);
return this.invokeDelete(url, data, config) as Promise<T>;
}
/**
* 将对象格式化为query string
* @param params 对象
* @param encode 是否encode,比如将中文编码为 unicode
*/
protected joinParams2QueryString(params: any, encode = true) {
if (params) {
const query: string[] = [];
for (const item in params) {
query.push(`${item}=${encode ? encodeURIComponent(params[item]) : params[item]}`);
}
return query.join('&');
}
return '';
}
protected formatArray2String(numbers: number[], noBrace = false) {
if (numbers.length) {
const v = JSON.stringify(numbers);
if (noBrace) {
return v.replace('[', '').replace(']', '');
}
return v;
}
return '';
}
protected formatStringArray2String(array: string[]) {
if (array.length) {
return JSON.stringify(array);
}
return '';
}
protected isDevEnvironment() {
return BaseService.isDev();
}
protected isProEnvironment() {
return BaseService.isPro();
}
public static isDev() {
return Envir.isDev();
}
public static isPro() {
return Envir.isPro();
}
}
export const enum Environment {
Dev,
Pro,
Test,
Stage,
Pre
}
export const enum EnvironmentText {
Dev = 'dev',
Pro = 'pro',
Test = 'test',
Stage = 'stage'
}
export class Envir {
public static isDev() {
return process.env.NODE_ENV === "development";
}
/**
* 正式线
*/
public static isPro() {
return process.env.NODE_ENV === "production" && !process.env.VUE_APP_HRS_ENV;
}
/**
* 正式线的镜像版本
*/
public static isStaging() {
return process.env.NODE_ENV === "production" && process.env.VUE_APP_HRS_ENV === 'stage';
}
/**
* 测试线
*/
public static isTesting() {
return process.env.NODE_ENV === "production" && process.env.VUE_APP_HRS_ENV === 'test';
}
public static getEnvir() {
if (this.isPro()) {
return Environment.Pro;
}
if (this.isTesting()) {
return Environment.Test;
}
if (this.isStaging()) {
return Environment.Stage;
}
return Environment.Dev;
}
public static getEnvirString() {
if (this.isPro()) {
return EnvironmentText.Pro;
}
if (this.isTesting()) {
return EnvironmentText.Test;
}
if (this.isStaging()) {
return EnvironmentText.Stage;
}
return EnvironmentText.Dev;
}
}
export class Uuid {
public static execute() {
const s: (number | string)[] = [];
const hexDigits = "0123456789abcdef";
for (let i = 0; i < 36; i++) {
s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
}
s[14] = "4";
s[19] = hexDigits.substr((+s[19] & 0x3) | 0x8, 1);
s[8] = s[13] = s[18] = s[23] = "-";
return s.join("");
}
}
/* eslint-disable */
import { Envir } from './env';
const enable = !Envir.isPro();
const logger = {
log: (msg: string | any, ...params: any[]) => {
enable && console.log(msg, params);
},
error: (msg: string | any) => {
console.error(msg);
}
};
export default logger;
/* eslint-disable */
import Axios from 'axios';
import { UserManager, Log, WebStorageStateStore, UserManagerSettings, User } from "oidc-client";
import config from "./config";
import qs from 'qs';
import { popupService } from '@/apppackage/element-upgrades/popup';
import { Envir, Uuid } from './env';
// import logger from './logger';
import BaseService from '@/api/core';
// import { isQianKunInstalled, qianKunGlobal } from './envir';
export interface Token {
access_token: string;
expires_in: number;
token_type: string;
/**
* 过期于,单位为s,如果要与 js 时间戳作比对,请将此值乘以1000
*/
expires_at: number;
}
let oidcInstance: UserManager | null = null;
function buildOidcClient() {
const options = {
...config.oidc,
monitorSession: false,
automaticSilentRenew: false,
userStore: new WebStorageStateStore({ store: window.localStorage })
} as UserManagerSettings;
return new UserManager(options);
}
function isChrome() {
const ua = window.navigator.userAgent;
if (ua) {
const lower = ua.toLowerCase();
if (lower.indexOf('chrome') > -1) {
const reg = /chrome\/(\d)+/i;
const m = reg.exec(lower);
if (m && m[0]) {
return +m[0].replace('chrome/', '')
}
}
}
return false;
}
export function isCurrentBrowserSafari() {
const ua = window.navigator.userAgent;
if (ua) {
const lower = ua.toLowerCase();
const safari = lower.indexOf('safari') > -1 && lower.indexOf('chrome') === -1;
if (safari) {
return safari;
}
return lower.indexOf('wxwork') > -1;
}
return false;
}
function setupOidcManager() {
if (!oidcInstance) {
Log.logger = console;
oidcInstance = buildOidcClient();
Log.logger.info(`oidc client setup`);
oidcInstance.events.addUserLoaded(function() {
Log.logger.info(`用户已登入`);
});
oidcInstance.events.addAccessTokenExpiring(function() {
Log.logger.error(`用户Token即将过期`);
});
oidcInstance.events.addAccessTokenExpired(function() {
Log.logger.error(`用户Token已过期,为其主动登出`);
const logoutAction = () => {
oidcInstance?.signoutRedirect()
.then(() => Log.info(`已登出`))
.catch(e => Log.error(`登出失败,${e}`));
}
oidcInstance?.signinSilent().then(t => {
if (!t) {
logoutAction();
}
}).catch(logoutAction);
});
oidcInstance.events.addSilentRenewError(() => {
Log.logger.error(`静默刷新失败`);
});
oidcInstance.getUser().then(u => {
if (u) {
const v = isChrome();
if (v && v >= 90) {
return oidcInstance?.signinSilent();
}
if (!isCurrentBrowserSafari() && !Envir.isDev()) {
oidcInstance?.events.addUserSignedOut(function() {
const logoutAction = () => {
oidcInstance?.signoutRedirect()
.then(() => (window.location.href = config.api.root))
.catch(Log.logger.error);
};
oidcInstance?.signinSilent().then(t => {
if (!t) {
logoutAction();
}
}).catch(logoutAction);
});
}
}
});
}
return oidcInstance;
}
function login() {
window.location.href = `${config.api.root}/login?redirect=${btoa(
encodeURIComponent(window.location.href)
)}`;
}
async function tryLogin(): Promise<void> {
return UserController.getUser().then(r => {
if (!UserController.checkAutoNav()) {
if (!r) {
login();
} else if (TokenManager.isExpired(r)) {
login();
}
}
});
}
export const enum TokenEvent {
UserChanegd = 1,
}
class UserController {
private static lastRenewTicks = 0;
public static readonly uid = Uuid.execute();
private static returnUrl = ""
private static readonly autoNav = 'autoNav';
public static readonly userTokenRenew = 'user-token-renew';
private static readonly untrimProperty = ['id_token', 'access_token', 'expires_at', 'profile', 'token_type'];
public static async tryLogin() {
return tryLogin();
}
public static setReturnUrl(url: string) {
this.returnUrl = url;
}
public static getReturnUrl() {
return this.returnUrl
}
public static async isUserLogined() {
const user = await UserController.getUser();
if (user) {
return !TokenManager.isExpired(user);
}
return false;
}
public static async getUser() {
const instance = setupOidcManager();
return await instance.getUser();
}
private static trimUser(user: User) {
const copy = Object.create(null);
for (const item of this.untrimProperty) {
copy[item] = (user as any)[item];
}
return copy;
}
public static trimUser2String(user: User) {
return JSON.stringify(this.trimUser(user));
}
public static async getUsername(): Promise<string> {
const user = await UserController.getUser();
if (user) {
return user.profile.name || user.profile.nickname || user.profile.realname || user.profile.mobile || user.profile.email || '[未设置名称]';
}
return '';
}
public static async getUserId(): Promise<string> {
const user = await UserController.getUser();
if (user) {
return user.profile.sub;
}
return '';
}
public static async getUserAvatar(): Promise<string> {
const user = await UserController.getUser();
if (user) {
return user.profile.avatar_url;
}
return '';
}
public static checkAutoNav() {
const auto = localStorage.getItem(this.autoNav) as string;
if (auto) {
localStorage.removeItem(this.autoNav);
setTimeout(() => window.location.href = auto);
return true;
}
return false;
}
public static async signout() {
const instance = setupOidcManager();
return instance.signoutRedirect();
}
public static removeUserCacheWithoutRedirect() {
localStorage.clear();
window.location.href = config.api.root;
}
public static removeUserCache(redirect?: string) {
localStorage.setItem(this.autoNav, redirect || window.location.href);
return this.signout();
}
public static async signin(params: any) {
const instance = setupOidcManager();
if (params) {
params = Object.assign(
{ state: { returnUrl: UserController.removeQueryParameter(window.location.href, 'authcode') } },
params
);
}
return instance.signinRedirect(params);
}
public static async getCurrentUserToken() {
const user = await UserController.getUser();
if (user) {
if (TokenManager.isExpired(user)) {
await UserController.tryLogin();
return '';
}
return "Bearer " + user.access_token;
}
return '';
}
public static async renewUserToken() {
const instance = setupOidcManager();
return instance.signinSilent({ silentRequestTimeout: 20 * 1000 });
}
public static removeQueryParameter(url: string, key: string) {
const split = url.split('?');
if (split.length <= 1) {
return url;
}
const search = split[split.length - 1];
const params = new URLSearchParams(search);
if (params.has(key)) {
params.delete(key);
}
const query = params.toString();
return `${split[0]}${query ? '?' + query : ''}`;
}
public static buildLoginKey() {
return `oidc.user:${config.oidc.authority}:${config.oidc.client_id}`;
}
}
interface AppToken extends Token {
appId: string | number;
}
export class TokenManager {
private static anonymousTokenCache: Token;
private static readonly appTokenKey = 'oidc.app';
private static userTokenCache: Token;
private static tokenFailed = false;
private static userTokenChangedEventHandlers: { key: string; action: Function }[] = [];
private static wrap(token: Token) {
return {
access_token: token.access_token,
expires_in: token.expires_in,
token_type: token.token_type,
expires_at: (Date.now() + token.expires_in * 1000) / 1000
};
}
public static isExpired(token: Token) {
return token.expires_at * 1000 <= Date.now();
}
public static removeAppTokenCache() {
localStorage.removeItem(this.appTokenKey);
}
/**
* 格式化token
* @param token token载体
* @param full 是否使用标准格式化(type+空格+token),为 false 时只返回 token 字符串
*/
public static format2String(token: Token, full = true) {
if (full) {
return `${token.token_type} ${token.access_token}`;
}
return token.access_token;
}
public static registerOnUserTokenChanged(key: string, action: (token: string) => void) {
const t = TokenManager.userTokenChangedEventHandlers.find(i => i.key === key);
if (t) {
t.action = action;
return;
}
TokenManager.userTokenChangedEventHandlers.push({ key, action });
}
public static unregisterOnUserTokenChanged(key: string) {
const t = TokenManager.userTokenChangedEventHandlers.find(i => i.key === key);
if (t) {
const index = TokenManager.userTokenChangedEventHandlers.indexOf(t);
TokenManager.userTokenChangedEventHandlers.splice(index, 1);
}
}
private static raiseOnUserTokenChanged(token: string) {
for (const item of TokenManager.userTokenChangedEventHandlers) {
item.action(token);
}
}
public static getUserTokenStringCache() {
if (TokenManager.userTokenCache) {
return TokenManager.format2String(TokenManager.userTokenCache);
}
return '';
}
public static async getUserTokenString(): Promise<string> {
if (TokenManager.userTokenCache) {
if (!TokenManager.isExpired(TokenManager.userTokenCache)) {
return TokenManager.format2String(TokenManager.userTokenCache);
}
}
const user = await UserController.getUser();
if (user) {
if (TokenManager.isExpired(user)) {
await UserController.renewUserToken();
const silentUser = await UserController.getUser();
if (silentUser) {
TokenManager.userTokenCache = silentUser;
const token = TokenManager.format2String(TokenManager.userTokenCache);
TokenManager.raiseOnUserTokenChanged(token);
return token;
}
throw new Error(`用户Token获取失败,请求已终止。`);
}
TokenManager.userTokenCache = user;
return TokenManager.getUserTokenString();
}
UserController.tryLogin();
return "";
}
public static async getAppToken(id?: string | number, force?: boolean) {
let key = id;
if (key) {
if (!force) {
const cache = this.reloadAppTokenFromCache(key);
if (cache) {
if (+cache.appId === +key) {
if (cache.expires_at * 1000 > new Date().valueOf()) {
return this.format2String(cache);
}
}
}
}
await this.getUserTokenString();
return this.format2String(await this.reloadAppToken(key, TokenManager.userTokenCache?.access_token));
}
return '';
}
private static async reloadAppToken(id: string | number, token: string): Promise<AppToken> {
if (!id || +id === 0) {
return Promise.reject(new Error(`App id cannot be empty or 0`));
}
return new Promise((resolve, reject) => {
Axios.post<Token>(`${config.oidc.authority}/connect/token`, qs.stringify({
client_id: config.api.clientId,
client_secret: config.api.clientSecret,
grant_type: "application",
scope: config.oidc.scope,
token: token || "",
app_register_id: id
}))
.then(r => {
if (r.data) {
const t = TokenManager.wrap(r.data);
const app = { ...t, appId: id };
this.writeToken2Cache(id, app);
resolve(app);
}
reject();
})
.catch(() => {
if (!this.tokenFailed) {
popupService.toast.warn('应用授权失败');
this.tokenFailed = true;
setTimeout(() => this.tokenFailed = false, 300);
}
reject(403);
});
});
}
private static reloadAppTokenFromCache(id: number | string) {
const local = localStorage.getItem(this.appTokenKey);
if (local) {
const list = JSON.parse(local) as { id: string | number; data: AppToken }[];
const t = _.find(list, i => +i.id === +id);
if (t) {
return t.data;
}
}
return null;
}
private static writeToken2Cache(id: number | string, data: AppToken) {
const local = localStorage.getItem(this.appTokenKey);
if (local) {
const list = JSON.parse(local) as { id: string | number; data: AppToken }[];
_.remove(list, i => +i.id === +id);
list.push({ id, data });
localStorage.setItem(this.appTokenKey, JSON.stringify(list));
} else {
localStorage.setItem(this.appTokenKey, JSON.stringify([{ id, data }]));
}
}
private static b64DecodeUnicode(str: string) {
return decodeURIComponent(atob(str).replace(/(.)/g, function(m, p) {
let code = p.charCodeAt(0).toString(16).toUpperCase();
if (code.length < 2) {
code = '0' + code;
}
return '%' + code;
}));
}
private static base64_url_decode(str: string) {
let output = str.replace(/-/g, "+").replace(/_/g, "/");
switch (output.length % 4) {
case 0:
break;
case 2:
output += "==";
break;
case 3:
output += "=";
break;
default:
throw "Illegal base64url string!";
}
try {
return this.b64DecodeUnicode(output);
} catch {
return atob(output);
}
};
public static decode(token: string) {
return JSON.parse(this.base64_url_decode(token.replace('Bearer ', '').split('.')[1]));
};
public static async getAnonymousTokenString(): Promise<string> {
if (TokenManager.anonymousTokenCache) {
if (!TokenManager.isExpired(TokenManager.anonymousTokenCache)) {
return TokenManager.format2String(TokenManager.anonymousTokenCache);
}
}
const r = await TokenManager.getAnonymousToken();
TokenManager.anonymousTokenCache = r;
return TokenManager.format2String(TokenManager.anonymousTokenCache);
}
public static getAnonymousToken(): Promise<Token> {
return new Promise((resolve, reject) => {
Axios.post<Token>(`${config.oidc.authority}/connect/token`, qs.stringify({
scope: config.api.scope,
client_id: config.api.clientId,
client_secret: config.api.clientSecret,
grant_type: "anonymous",
}))
.then(r => {
if (r.data) {
return resolve(TokenManager.wrap(r.data));
}
return reject("Cannot revoke the token.");
})
.catch(reject);
});
}
}
export { setupOidcManager, UserController };
<pre>
<code>
<hrs-button class="pre-icon pre-icon-email">按钮带Email图标</hrs-button>
<hrs-button class="pre-icon pre-icon-back">按钮带返回图标</hrs-button>
</code>
</pre>
<template>
<el-button
class="fs-button"
:class="innerClass"
v-bind="$attrs"
@click="onClick"
>
<slot></slot>
</el-button>
</template>
<script lang="ts">
import { Vue, Component, Prop } from "vue-property-decorator";
/**
* 支持的 type 值,默认值为 default.
*/
const BUTTON_TYPES = [
"primary",
"default",
"info",
"plain",
"text",
"text-primary",
"text-info",
"icon",
"danger",
];
/**
* 支持的 size 值,默认值为 default
*/
const BUTTON_SIZES = ["extra-large", "large", "default", "medium", "small"];
@Component({ name: "FsButton" })
export default class HrsButton extends Vue {
@Prop({
required: false,
default: "default",
validator: (v) => !v || BUTTON_TYPES.indexOf(v) > -1,
})
private type!: string;
@Prop({
required: false,
default: "default",
validator: (v) => !v || BUTTON_SIZES.indexOf(v) > -1,
})
private size!: string;
@Prop({ required: false, default: undefined })
private autoWidth!: boolean;
/**
* 圆角属性,默认为有圆角 4px,如果有赋值则圆角则为 20px
*/
@Prop({
required: false,
default: "default",
validator: (v) => !v || v === "default",
})
private radius!: string;
private get innerClass() {
return (
`fs-button--${this.size} fs-button-type--${this.type}` +
(this.radius === "default" ? " default-radius" : "") +
(this.autoWidth !== undefined ? ` auto-width` : "")
);
}
private onClick(e: MouseEvent) {
this.$emit("click", e);
}
}
</script>
<style lang="less" scoped>
@import "~@/css/element-ui-override/fs-button.less";
</style>
<template>
<div class="nums-input">
<input type="number" v-model="value" placeholder="组合数量" />
<div class="operated-button">
<span
class="increase"
:class="{ disabled: max * 1 <= value * 1 }"
@click="onIncrease"
>
<i class="el-icon-arrow-up"></i>
</span>
<span
class="decrease"
:class="{ disabled: min * 1 >= value * 1 }"
@click="onDecrease"
>
<i class="el-icon-arrow-down"></i
></span>
</div>
</div>
</template>
<script lang="ts">
import { Component, Vue, Prop, Watch } from "vue-property-decorator";
import { popupService } from "./popup";
@Component({ components: {} })
export default class InputNumsNew extends Vue {
@Prop({ default: 0 })
private min!: number;
@Prop({ default: 999 })
private max!: number;
private value = 0;
@Watch("value")
private onValueChange() {
if (this.min !== undefined && this.min > this.value * 1) {
this.value = this.min;
this.warnMessage2Min();
}
if (this.max !== undefined && this.max < this.value * 1) {
this.value = this.max;
this.warnMessage2Max();
}
}
private onIncrease() {
if (this.max === undefined || this.max > this.value) {
this.value = this.value * 1 + 1;
}
}
private onDecrease() {
if (this.max === undefined || this.min < this.value) {
this.value = this.value * 1 - 1;
}
}
private warnMessage2Max() {
popupService.toast.warn(`当前数量超过最大值${this.max}`);
}
private warnMessage2Min() {
popupService.toast.warn(`当前数量超过最小值${this.min}`);
}
}
</script>
<style lang="less" scoped>
@import "~@/css/variables.less";
.nums-input {
width: 117px;
position: relative;
overflow: hidden;
input {
width: 100%;
height: 32px;
padding-left: 10px;
padding-right: 34px;
outline: none;
border: 1px solid #e6e6e6;
border-radius: 2px;
transition: all 0.5s;
line-height: 32px;
&:focus {
transition: all 0.5s;
border: 1px solid @mainColor;
}
}
input::-webkit-input-placeholder {
color: #ccc;
}
.operated-button {
position: absolute;
top: 1px;
bottom: 1px;
right: 1px;
span {
display: block;
width: 18px;
height: 15px;
line-height: 15px;
cursor: pointer;
text-align: center;
&.increase {
border-left: 1px solid #e6e6e6;
border-bottom: 1px solid #e6e6e6;
}
&.decrease {
border-left: 1px solid #e6e6e6;
}
i {
color: #999;
font-size: 13px;
transform: scale(0.8);
line-height: 1;
}
&:hover {
i {
color: @mainColor;
}
}
&.disabled {
cursor: not-allowed;
i {
color: #999;
}
}
}
}
}
</style>
<template>
<el-input-number
class="el-input-nums"
v-bind="$attrs"
v-on="$listeners"
:value="value"
@blur="onBlur"
@focus="onFocus"
:class="[{ 'no-empty': value }, { focus: focus2Current }]"
>
</el-input-number>
</template>
<script lang="ts">
import { Vue, Component, Model } from "vue-property-decorator";
@Component({ name: "HrsInput" })
export default class HrsInputNumber extends Vue {
@Model("input", { required: true })
private value!: string | number;
private focus2Current = false;
private onBlur() {
this.focus2Current = false;
}
private onFocus() {
this.focus2Current = true;
}
}
</script>
<style lang="less">
@import "~@/css/variables.less";
@no-empty-color: #ffffff;
.el-input-nums {
&.focus,
&.no-empty {
input {
background-color: @no-empty-color;
}
}
&.el-input-number {
.el-input-number__decrease,
.el-input-number__increase {
width: 17px;
background-color: #fff;
}
.el-input {
input {
text-align: left;
}
}
}
}
</style>
<template>
<el-input
ref="innerElInput"
class="fs-input"
@input="onInput"
@clear="onClear"
@blur="onBlur"
v-bind="$attrs"
:value="value"
@focus="onFocus"
:class="[innerClass, { 'no-empty': value }, { focus: focus2Current }]"
>
<template slot="prefix">
<slot name="prefix"></slot>
</template>
<template slot="suffix">
<slot name="suffix"></slot>
</template>
<template slot="prepend">
<slot name="prepend"></slot>
</template>
<template slot="append">
<slot name="append"></slot>
</template>
</el-input>
</template>
<script lang="ts">
import { Vue, Component, Prop, Model, Ref } from "vue-property-decorator";
/**
* 支持的 type 值,默认值为 default.
*/
const INPUT_TYPES = ["default", "underline", "round", "plain"];
@Component({ name: "HrsInput" })
export default class HrsInput extends Vue {
/**
* 这里用 shape 来命名是因为 input 本身就有type = text/password/textarea 等值来控制器其特定逻辑
*/
@Prop({
required: false,
default: "default",
validator: (v) => !v || INPUT_TYPES.indexOf(v) > -1,
})
private shape!: string;
private focus2Current = false;
@Model("input", { required: true })
private value!: string | number;
@Prop({ required: false })
private autofocus!: boolean;
@Ref() private innerElInput!: { focus: () => void };
private get innerClass() {
return `fs-input--${this.shape}`;
}
private onInput(v: string | number) {
this.$emit("input", v);
}
private onClear(e: MouseEvent) {
this.$emit("clear", e);
}
private onBlur(e: MouseEvent) {
this.focus2Current = false;
this.$emit("blur", e);
}
private onFocus(e: MouseEvent) {
this.focus2Current = true;
this.$emit("focus", e);
}
mounted() {
if (this.autofocus !== undefined) {
this.focus();
}
}
focus() {
if (this.innerElInput) {
this.innerElInput.focus();
}
}
}
</script>
<style lang="less">
@import "~@/css/variables.less";
@border-color: #e6e6e6;
@background-color: #ffffff;
@no-empty-color: #ffffff;
@disabled-color: #f5f7fa;
@count-color: #cccccc;
@placeholder-color: #cccccc;
.fs-input {
border-color: @border-color;
.el-input__inner {
height: 38px;
border-radius: 4px;
}
&.fs-input--default {
.el-input__inner,
.el-textarea__inner {
background-color: @background-color;
}
}
&.el-textarea {
border-radius: 4px;
.el-input__count {
font-size: 14px;
height: 17px;
right: 12px;
bottom: 14px;
line-height: 20px;
color: #888;
}
}
&.focus,
&.no-empty {
&.fs-input--default {
.el-input__inner,
.el-textarea__inner {
background-color: @no-empty-color;
}
}
}
&.is-disabled {
&.fs-input--default {
.el-input__inner,
.el-textarea__inner {
background-color: @disabled-color;
}
}
}
input::-webkit-input-placeholder {
font-size: 14px;
color: @placeholder-color;
line-height: 24px;
}
textarea::-webkit-input-placeholder {
font-size: 14px;
color: @placeholder-color;
line-height: 24px;
}
&.is-disabled {
.el-input__inner {
&:hover,
&:focus,
&:active {
border-color: @border-color;
}
}
}
.el-input__inner {
border-radius: 2px;
&:hover,
&:focus,
&:active {
border-color: @mainColor;
}
}
&.fs-input--underline {
.el-input__inner {
padding-left: 0;
border: none;
border-bottom: 1px solid @border-color;
& + .el-input__prefix {
left: -5px;
}
}
&.el-input--prefix {
.el-input__inner {
padding-left: 20px;
}
}
.el-input__inner {
&:hover,
&:focus,
&:active {
border-color: @mainColor;
}
}
&.is-disabled {
.el-input__inner {
&:hover,
&:focus,
&:active {
border-color: @border-color;
}
}
}
}
&.fs-input--plain {
.el-input__inner {
border-color: transparent;
}
.el-input__inner {
&:hover,
&:focus,
&:active {
border-color: @mainColor;
}
}
&.is-disabled {
.el-input__inner {
&:hover,
&:focus,
&:active {
border-color: transparent;
}
}
}
}
.el-textarea__inner {
border-radius: 2px;
&:focus {
border-color: @mainColor;
}
}
}
.show-limit {
.el-input__suffix {
// height: 17px;
// bottom: -22px;
right: 12px;
// top: auto;
.el-input__count {
.el-input__count-inner {
font-size: 14px;
padding: 0px;
color: #888888;
}
}
}
}
.el-input,
.el-select {
input {
background-color: @background-color;
}
&.no-empty {
input {
background-color: @no-empty-color;
}
}
}
.el-input {
input {
padding-left: 10px;
}
}
</style>
import Vue, { CreateElement, VNode } from 'vue';
import { MessageBox, Message } from 'element-ui';
import { MessageBoxData, ElMessageBoxOptions } from 'element-ui/types/message-box';
import { ElMessageOptions, ElMessageComponent } from 'element-ui/types/message';
import logger from '@/api/logger';
export interface ConfirmDialogOption {
title?: string;
/**
* 默认为 warning, 可选参数。如果不想icon 显示,请将 hideIcon 置为 true
*/
type?: 'success' | 'warning';
/**
* 主描述
*/
primaryText: string;
/**
* 次级描述文字,可选
*/
secondaryText?: string;
/**
* 主按钮文字,默认为“确认”
*/
primaryButtonText?: string;
/**
* 次级按钮文字,默认为“取消”
*/
secondaryButtonText?: string;
/**
* 主按钮默认位置,默认居左,此值默认 false
*/
primaryButtonRight?: boolean;
/**
* 默认不同类型的 confirm 框主体内容显示会有一个图标,如果此值为 true,图标会被隐藏,默认此值为 false
*/
hideIcon?: boolean;
hidePrimaryButton?: boolean;
hideSecondaryButton?: boolean;
/**
* 描述性文字(包括主描述和次级描述)是否根据 type 不同有自己的颜色。
* 此属性在 hideIcon 为 true 时不起作用。
* 此属性只在 type 为 warning 时生效。
*/
textColored?: boolean;
/**
* 是否在窗口打开时自动给第一个能获得焦点的元素聚焦,默认 false
*/
autoFocusWhenDialogOpened?: boolean;
/**
* 是否将message作为html渲染
*/
useHtml?: boolean;
hideClose?: boolean;
}
export interface ToastService {
info(message: string): void;
warn(message: string): void;
success(message: string): void;
message(option: ElMessageOptions): void;
error(e: any, message?: string): void;
}
export interface PopupService {
confirm(option: ConfirmDialogOption): Promise<MessageBoxData>;
noop: () => void;
toast: ToastService;
}
interface CachedSuccessMessage {
message: string;
component: ElMessageComponent;
}
const customMessageBoxClassPrefix = 'fs-message-box-';
const vue = new Vue();
const render: CreateElement = vue.$createElement;
const messageType2Class: { [key: string]: string } = {
default: 'icon-default',
success: 'icon-success',
warning: 'icon-warning'
};
const buttonClasses = ['fs-button', 'fs-button--large'];
const primaryButtonClasses = [...buttonClasses, 'fs-button-type--primary'].join(' ');
const secondaryButtonClasses4Success = [...buttonClasses, 'fs-button-type--default'].join(' ');
const secondaryButtonClasses4Default = [...buttonClasses, 'fs-button-type--plain'].join(' ');
const successMessageCache: CachedSuccessMessage[] = [];
const service: PopupService = {
confirm: (option: ConfirmDialogOption) => {
const customClasses = ['custom'];
const elOption: ElMessageBoxOptions = {
title: option.title,
closeOnClickModal: false,
confirmButtonText: option.primaryButtonText || '确定',
cancelButtonText: option.secondaryButtonText || '取消',
type: option.type || 'warning',
confirmButtonClass: primaryButtonClasses,
cancelButtonClass: option.type === 'success' ? secondaryButtonClasses4Success : secondaryButtonClasses4Default,
showCancelButton: !option.hideSecondaryButton,
showConfirmButton: !option.hidePrimaryButton,
dangerouslyUseHTMLString: option.useHtml,
showClose: !option.hideClose,
};
if (option.hideSecondaryButton || option.hidePrimaryButton) {
customClasses.push(`hide-secondary-button`);
}
if (option.primaryButtonRight) {
const current = elOption.confirmButtonClass;
elOption.confirmButtonClass = elOption.cancelButtonClass;
elOption.cancelButtonClass = current;
} else {
customClasses.push('reverse-button-direction');
}
customClasses.push(messageType2Class.default);
customClasses.push(messageType2Class[elOption.type as string]);
customClasses.push(option.title ? 'has-title' : 'hide-title');
if (option.hideIcon) {
customClasses.push('hide-icon');
} else {
if (option.type === 'warning' && option.textColored) {
customClasses.push('text-colored');
}
if (option.title) {
customClasses.push(`has-title-icon`);
}
}
const nodes: VNode[] = [];
nodes.push(render('p', {
staticClass: `${customMessageBoxClassPrefix}-primary-text`,
domProps: { innerHTML: option.primaryText }
}));
if (option.secondaryText) {
nodes.push(render('p', {
staticClass: `${customMessageBoxClassPrefix}-secondary-text`,
domProps: { innerHTML: option.secondaryText }
}));
} else {
customClasses.push('primary-message-text');
}
elOption.message = render('div', nodes);
elOption.customClass = customClasses.map(i => `${customMessageBoxClassPrefix}-${i}`).join(' ');
if (!option.autoFocusWhenDialogOpened) {
setTimeout(() => {
const element = document.activeElement as HTMLElement;
if (element) {
const parent = element.parentNode as HTMLElement;
if (parent && parent.classList && parent.classList.contains('el-message-box__btns')) {
element.blur();
}
}
}, 50);
}
const openModal = (open: boolean) => {
const body = document.querySelector('body');
if (body) {
if (open) {
body.classList.add('modal-active');
} else {
body.classList.remove('modal-active');
}
}
};
openModal(true);
return MessageBox.confirm('', elOption).finally(() => openModal(false));
},
noop: () => { return false; },
toast: {
info: message => Message({ message, type: 'info', showClose: true }),
warn: message => Message({ message, type: 'warning', showClose: true }),
success: message => {
const same = successMessageCache.find(i => i.message === message);
if (same) {
return;
}
const component = Message({
message,
type: 'success',
showClose: true,
onClose: () => {
const index = successMessageCache.findIndex(i => i.component === component);
if (index > -1) {
successMessageCache.splice(index, 1);
}
}
});
successMessageCache.push({ message, component });
},
message: option => Message(option),
error: (e, message) => {
const m = message || e.message || e.msg || (_.isString(e) && e) || '操作失败';
if (/com.(.*)exception/i.test(m)) {
logger.error(m);
service.toast.warn('系统出现异常');
} else {
service.toast.warn(m);
}
}
}
};
export { service as popupService };
<template>
<el-select
v-bind="$attrs"
v-on="$listeners"
:value="value"
@blur="onBlur"
@focus="onFocus"
:popper-class="innerClass"
>
<slot></slot>
</el-select>
</template>
<script lang="ts">
import { Vue, Component, Model, Prop } from "vue-property-decorator";
@Component({ name: "HrsSelect", inheritAttrs: false })
export default class HrsInput extends Vue {
@Model("input", { required: true })
private value!: string | number;
@Prop()
private innerClass!: string;
private focus2Current = false;
private isEmpty(value: any) {
return value === "" || value === undefined || value === null;
}
private onBlur(e: MouseEvent) {
this.focus2Current = false;
this.$emit("blur", e);
}
private onFocus() {
this.focus2Current = true;
}
}
</script>
<style lang="less">
@import "~@/css/variables.less";
@background-color: #fafafa;
@no-empty-color: #ffffff;
.el-input,
.el-select {
input {
background-color: @no-empty-color;
}
// &.focus,
// &.no-empty {
// input {
// background-color: @no-empty-color;
// }
// }
}
</style>
......@@ -8,4 +8,7 @@ export enum Direction {
ApplicationSetting,
ManagerSetting,
ManagerLog,
Redirect,
SilentRedirect,
}
......@@ -4,6 +4,7 @@ import { Dictionary } from "vue-router/types/router";
import { Direction } from './direction';
import { MetaHeader } from './meta-header';
import { setCompatibleUrl } from './compatible-url';
import { loginRouters } from "@/router/sub-router/login";
Vue.use(VueRouter);
......@@ -12,6 +13,7 @@ const routes: Array<RouteConfig> = [
path: "",
redirect: "/workadmin/home"
},
...loginRouters,
{
path: '/workadmin',
component: () => import('@/views/pages/main.vue'),
......
export class MetaHeader {
public static readonly root = '组织管理后台';
public static readonly home = '首页';
public static readonly addressBook = '通讯录';
public static readonly applicationRecord = '申请加入记录';
......
// import { Direction } from '../direction';
// import { MetaHeader } from '../meta-header';
export const loginRouters = [
// {
// path: "/login",
// meta: { direction: Direction.Login, title: MetaHeader.root },
// component: () => import("@/views/login/index.vue"),
// },
// {
// path: "/redirect",
// meta: { direction: Direction.Redirect2Domain, title: MetaHeader.root },
// component: () => import("@/views/login/custom-domain-nav.vue"),
// },
// {
// path: "/oidc-redirect",
// meta: { direction: Direction.Redirect, title: MetaHeader.root },
// component: () => import("@/views/login/redirect.vue"),
// },
// {
// path: "/oidc-silent-redirect",
// meta: { direction: Direction.SilentRedirect, title: MetaHeader.root },
// component: () => import("@/views/login/silent-redirect.vue"),
// },
// {
// path: '/signout',
// component: () => import('@/views/login/signout.vue'),
// meta: { direction: Direction.Signout, title: MetaHeader.root }
// },
// {
// path: '/authcode',
// component: () => import('@/views/components/authcode-host'),
// meta: { direction: Direction.Authcode, title: MetaHeader.root }
// },
// {
// path: '/upgrade',
// component: () => import('@/views/error/upgrade.vue'),
// meta: { direction: Direction.Upgrading, title: MetaHeader.root }
// },
// {
// path: '/error',
// component: () => import('@/views/error/index.vue'),
// children: [
// {
// path: '403',
// component: () => import('@/views/error/403.vue'),
// meta: { direction: Direction.Error403, title: MetaHeader.root }
// },
// {
// path: '404',
// component: () => import('@/views/error/404.vue'),
// meta: { direction: Direction.Error404, title: MetaHeader.root }
// },
// {
// path: '500',
// component: () => import('@/views/error/500.vue'),
// meta: { direction: Direction.Error500, title: MetaHeader.root }
// }
// ]
// },
// {
// path: '/how-to-adjust-system-time',
// component: () => import('@/views/error/adjust-system-time.vue'),
// meta: { direction: Direction.AdjustTime, title: MetaHeader.root }
// },
];
<template>
<div
v-loading="!error"
class="
d-flex
redirecting
justify-content-center
align-items-center
flex-fill
"
:class="{ bg: error }"
>
{{ error }}
<div v-if="error">。点击<a href="/">这里</a>返回首页重试。</div>
</div>
</template>
<script lang="ts">
import { Component, Vue } from "vue-property-decorator";
import { setupOidcManager, TokenManager } from "@/api/token";
import config from "@/api/config";
@Component({ components: {} })
export default class Redirect extends Vue {
private error = "";
mounted() {
document.title = "登录中...";
const oidc = setupOidcManager();
oidc.getUser().then((u) => {
if (u && !TokenManager.isExpired(u)) {
return (window.location.href = config.api.root);
}
oidc.signinRedirectCallback()
.then((user) => {
const remote = user.state?.returnUrl;
window.location.href = remote || config.api.root;
})
.catch((error) => (this.error = `登录失败,${error}`));
});
}
}
</script>
<style lang="less" scoped>
.redirecting {
font-size: 30px;
padding: 400px 50px;
}
</style>
<template>
<div></div>
</template>
<script lang="ts">
import { setupOidcManager } from "@/api/token";
import logger from "@/core-ui/controller/logger";
import { Component, Vue } from "vue-property-decorator";
@Component({ components: {} })
export default class SilentRedirect extends Vue {
mounted() {
const oidc = setupOidcManager();
oidc.signinSilentCallback().then().catch(logger.error);
}
}
</script>
<style lang="less" scoped>
.login-message {
font-size: 25px;
padding-top: 25%;
text-align: center;
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment