@opentiny/vue-renderless
Version:
An enterprise-class UI component library, support both Vue.js 2 and Vue.js 3, as well as PC and mobile.
534 lines (533 loc) • 17.1 kB
JavaScript
import {
__spreadProps,
__spreadValues
} from "../chunk-G2ADBYYC.js";
import { debounce } from "@opentiny/utils";
import { toDateStr } from "@opentiny/utils";
import { toJsonStr } from "@opentiny/utils";
import { toJson } from "@opentiny/utils";
import { logger } from "@opentiny/utils";
const toLowerCase = (val) => {
return typeof val === "string" ? val.toLowerCase() : val;
};
const getUserById = (obj, id) => {
return obj && obj[toLowerCase(id)];
};
const getLowerCaseObj = (obj) => {
const newObj = {};
Object.keys(obj).forEach((key) => {
newObj[toLowerCase(key)] = obj[key];
});
return newObj;
};
const request = {
timmer: null,
group: {},
requests: [],
cache: {},
batch: 0,
addRequest({ param, cb }) {
this.requests.push({ param, cb, result: [] });
const { valueField, queryIds } = param;
if (!this.group[valueField]) {
this.group[valueField] = [];
}
queryIds.forEach((id) => {
if (!~this.group[valueField].indexOf(id)) {
this.group[valueField].push(id);
}
});
},
removeRequest(item) {
const idx = this.requests.indexOf(item);
this.requests.splice(idx, 1);
},
clearRequest() {
this.timmer = null;
this.group = {};
this.requests = [];
this.cache = {};
this.batch = 0;
},
getParams() {
const args = [];
for (let key in this.group) {
const all = this.group[key];
const items = this.splitArr(all, this.batch);
if (key === "userId") {
args.push(...items.map((item) => item.join(",")));
} else {
args.push(
...items.map((item) => ({
valueField: key,
valueSplit: item.valueSplit,
[key]: item.join(",")
}))
);
}
}
return args;
},
setCache(data, valueField) {
const me = this;
if (valueField && !this.group[valueField]) {
this.group[valueField] = [];
}
data.forEach((item) => {
for (let key in this.group) {
if (!me.cache[key])
me.cache[key] = {};
me.cache[key][toLowerCase(item[key])] = item;
}
});
},
singleRequest(param, api, cb) {
const { queryIds, valueField, valueSplit } = param;
const queryStr = queryIds.join(",");
const params = valueField === "userId" ? queryStr : { valueField, valueSplit, [valueField]: queryStr };
api.fetchW3Accounts(params).then((data) => {
cb(data);
}).catch((e) => {
cb({ error: e });
});
},
batchRequest(api, emit) {
const me = this;
const reqParamsSeq = me.getParams();
let reqLen = reqParamsSeq.length;
let final = true;
const onFinally = () => {
if (final) {
final = false;
reqLen--;
const errors = [];
if (!reqLen) {
this.requests.forEach(({ cb, result, param }) => {
const { queryIds, valueField } = param;
cb(result);
queryIds.forEach((id) => {
if (!getUserById(this.cache[valueField], id)) {
errors.push(id);
}
});
});
errors.length && logger.warn(`user [${errors.join(",")}] not found`);
emit("error", errors);
this.clearRequest();
}
}
};
reqParamsSeq.forEach((params) => {
api.fetchW3Accounts(params).then((data) => {
me.setCache(data);
me.requests.slice().forEach((reqItem) => {
const { param, cb } = reqItem;
const { queryIds, valueField } = param;
queryIds.forEach((id) => {
const user = getUserById(me.cache[valueField], id);
user && !reqItem.result.includes(user) && reqItem.result.push(user);
});
if (reqItem.result.length === queryIds.length) {
me.removeRequest(reqItem);
cb(reqItem.result);
}
});
}).then(onFinally).catch(onFinally);
});
},
setBatch(batch) {
if (typeof batch !== "number" || batch <= 0) {
batch = 50;
}
if (batch <= 500 && this.batch < batch) {
this.batch = batch;
}
},
getusers({ param, api, batch, cb, emit }) {
if (batch !== false) {
this.setBatch(batch);
clearTimeout(this.timmer);
this.addRequest({ param, cb });
this.timmer = setTimeout(() => this.batchRequest(api, emit), 100);
} else {
this.singleRequest(param, api, cb);
}
},
splitArr(arr, num) {
let result = [];
if (arr.length && num > 0) {
const just = arr.length % num === 0;
const part = Math.floor(arr.length / num);
const len = just ? part : part + 1;
for (let i = 0; i < len; i++) {
result.push(arr.slice(i * num, (i + 1) * num));
}
}
return result;
}
};
const suggestUser = (api) => (query) => {
return new Promise((resolve, reject) => {
api.fetchSuggestUser(query).then((users) => {
if (users.length) {
resolve(users);
} else {
resolve({ invalid: query });
}
}).catch(reject);
});
};
const updateOptions = ({ props, state, nextTick }) => (usersList) => {
const { noDataText } = props;
const { valueField, textField } = state;
const values = [].concat(state.user || []);
const options = [].concat(state.selected);
const hides = [];
let count = 0;
usersList.forEach((users) => {
users.forEach((user) => {
const u = user[valueField];
user._show = true;
count++;
if (!~values.indexOf(u)) {
values.push(u);
options.push(user);
} else {
hides.push(u);
}
});
});
state.selected.forEach((us) => {
us._show = !!~hides.indexOf(us[valueField]);
});
!count && !noDataText && (state.visible = false);
if (!props.sortByFetchData) {
options.sort((a, b) => a[textField] > b[textField] ? 1 : -1);
}
state.options = options;
return nextTick();
};
const autoSelect = ({ props, state, nextTick }) => (usersList) => {
if (!props.autoSelect || !usersList.length || props.multiple && props.multipleLimit && state.user.length >= props.multipleLimit) {
return nextTick();
}
const values = props.multiple ? [].concat(state.user) : [];
usersList.forEach((list) => {
if (list.length === 1) {
const value = list[0][state.valueField];
!~values.indexOf(value) && values.push(value);
if (props.autoClose) {
state.visible = false;
}
}
});
if (!values.length) {
return nextTick();
}
if (props.multiple) {
state.user.length !== values.length && (state.user = values);
} else {
state.user = values[0];
}
return nextTick();
};
const searchMethod = ({ api, props, state, emit }) => debounce(props.delay, (query) => {
if (query && query.trim().length >= props.suggestLength) {
state.loading = true;
state.visible = true;
const { multiple } = props;
let suggests;
if (multiple) {
suggests = query.split(new RegExp(`[${props.textSplit.split("").join("\\")}]`)).filter((q) => q && q.length >= props.suggestLength).map((q) => api.suggestUser(q.trim()));
} else {
suggests = [api.suggestUser(query.trim())];
}
Promise.all(suggests).then((users) => {
const failList = [];
const usersList = [];
users.forEach((item) => {
if (item.invalid) {
failList.push(item.invalid);
} else {
usersList.push(item);
}
});
if (failList.length)
emit("error", failList);
api.updateOptions(usersList).then(() => {
api.autoSelect(usersList).then(() => {
api.userChange(state.user);
state.loading = false;
});
});
});
}
});
const setSelected = ({ api, props, state }) => (value) => {
const values = Array.isArray(value) ? value.map((v) => (v + "").toLocaleLowerCase()) : (value + "").toLocaleLowerCase().split(props.valueSplit);
state.selected = state.options.filter((user) => {
return ~values.indexOf((user[state.valueField] + "").toLocaleLowerCase());
});
props.cache && api.cacheUser(state.selected);
};
const userChange = ({ api, emit, props, state, dispatch, constants }) => (value, emitChangeFlag = true) => {
const { multiple } = props;
let newVal = multiple && Array.isArray(value) ? value.join(props.valueSplit) : (value || "") + "";
api.setSelected(newVal);
if (typeof state.lastValue === "string" && state.lastValue !== null && state.lastValue.toLocaleLowerCase() !== newVal.toLocaleLowerCase()) {
emit("update:modelValue", newVal);
if (emitChangeFlag) {
emit("change", newVal, state.selected);
dispatch(constants.COMPONENT_NAME.FormItem, constants.EVENT_NAME.FormChange, newVal);
}
}
state.lastValue = newVal;
};
const syncCacheIds = ({ props, state }) => (ids, queryIds, cacheData) => {
const { cacheFields, cacheKey } = props;
const { valueField } = state;
const cacheUsers = toJson(window.localStorage.getItem(cacheKey)) || {};
const caseCacheUsers = getLowerCaseObj(cacheUsers);
ids.forEach((id) => {
const caseId = toLowerCase(id);
const cacheUser2 = caseCacheUsers[caseId];
if (cacheUser2) {
const textField = state.textField === "userCN" || state.textField === "userId" || state.textField === "dept" ? "" : state.textField;
if (textField !== "" && !cacheUser2.a) {
window.localStorage.removeItem(cacheKey);
queryIds.push(id);
}
const user = {
userId: cacheUser2.i,
userCN: cacheUser2.u,
dept: cacheUser2.d,
employeeNumber: cacheUser2.e,
[textField]: cacheUser2.a
};
cacheFields.forEach((field) => {
user[field] = cacheUser2[field];
});
cacheData.push(
Object.assign(user, {
[valueField]: cacheUser2.p || cacheUser2.i
})
);
} else {
queryIds.push(id);
}
});
};
const getUsers = ({ api, props, state, emit }) => (value) => {
const { cache } = props;
const { valueField } = state;
const ids = Array.isArray(value) ? value : value.split(props.valueSplit);
const cacheData = [];
const queryIds = cache ? [] : ids;
if (cache) {
api.syncCacheIds(ids, queryIds, cacheData);
request.setCache(cacheData, valueField);
if (!queryIds.length) {
return Promise.resolve(cacheData);
}
}
const param = { valueSplit: props.valueSplit, valueField, queryIds };
return new Promise((resolve, reject) => {
const cb = (data) => {
if (data.error) {
reject(data.error);
} else {
const filterData = cacheData.filter((cache2) => !~data.findIndex((d) => d[valueField] === cache2[valueField]));
resolve(data.concat(filterData));
}
};
request.getusers({ param, api, batch: state.batch, cb, emit });
});
};
const updateCache = ({ props, state }) => () => {
const users = toJson(window.localStorage.getItem(props.cacheKey)) || {};
const currDate = toDateStr(/* @__PURE__ */ new Date(), "yyyyMMdd");
if (currDate !== users.t) {
users.t = currDate;
for (let u in users) {
if (u !== "t") {
let user = users[u];
if (user.r > 0) {
user.r = 0;
} else {
user.r--;
}
}
}
}
state.cache = users;
};
const saveCache = ({ props }) => (cache) => {
window.localStorage.setItem(props.cacheKey, toJsonStr(cache));
};
const cacheUser = ({ api, props, service, state }) => (users) => {
const { cacheKey } = props;
const { valueField } = state;
const cacheUser2 = toJson(window.localStorage.getItem(cacheKey)) || {};
const cacheFields = service.userCache;
let user;
for (let i = 0; i < users.length; i++) {
const u = users[i];
const key = u[valueField];
user = cacheUser2[key];
if (user) {
user.r++;
} else {
const us = {
p: u[valueField],
// 增加一个主键
i: u[cacheFields.userId],
u: u[cacheFields.userCN],
d: u[cacheFields.dept],
e: u[cacheFields.eno],
a: ~["userCN", "userId", "dept"].indexOf(state.textField) ? null : u[state.textField],
r: 0
};
props.cacheFields.forEach((field) => {
us[field] = u[field];
});
cacheUser2[key] = us;
}
}
try {
api.saveCache(cacheUser2);
} catch (e) {
const sortUsers = Object.keys(cacheUser2).sort((a, b) => {
return cacheUser2[a].r < cacheUser2[b].r ? 1 : -1;
});
const cutUser = sortUsers.splice(0, sortUsers.length / 2);
const newCache = {};
for (let i in cutUser) {
newCache[cutUser[i]] = cacheUser2[cutUser[i]];
}
api.saveCache(newCache);
}
};
const useSortable = ({ api, props, state, vm }) => () => {
const selectDom = vm.$refs.select.$el;
if (props.sortable && props.multiple && !state.sortable) {
const tagsDom = selectDom.querySelector(".tiny-select__tags>span>span");
state.sortable = props.sortable.create(tagsDom, {
handle: ".tiny-tag",
ghostClass: "tiny-user__ghost",
onEnd: ({ newIndex, oldIndex }) => {
let currUser = state.user.splice(oldIndex, 1)[0];
state.user.splice(newIndex, 0, currUser);
api.userChange(state.user);
}
});
}
};
const visibleChange = ({ state, emit }) => (show) => {
if (!show) {
state.visible = show;
state.options = state.selected.map((user) => Object.assign(user, { _show: true }));
}
emit("visible-change", show);
};
const initUser = ({ api, props, state }) => (value) => {
if (value === state.lastValue && value !== null) {
return;
}
state.user = !props.multiple ? "" : [];
if (typeof value === "number") {
value += "";
}
if (!value) {
state.options = [];
state.selected = [];
api.userChange(value, props.changeCompat);
return;
}
value && api.getUsers(value).then((info) => {
info.sort((a, b) => {
return value.indexOf(a[state.valueField] + "") > value.indexOf(b[state.valueField] + "") ? 1 : -1;
});
const list = info.map((user) => {
user._show = true;
return user[state.valueField];
});
state.options = info;
state.user = props.multiple ? list : list[0];
api.userChange(value, props.changeCompat);
});
};
const handleBlur = ({ constants, dispatch, state, emit }) => (e) => {
dispatch(constants.COMPONENT_NAME.FormItem, constants.EVENT_NAME.FormBlur, state.user);
emit("blur", e);
};
const initService = ({ props, service }) => {
const noopFnCreator = (propName) => () => {
if (propName) {
return Promise.reject(
new Error(`[TINY Error][User]] Prop ${propName} is mandatory when the framework service is not used`)
);
} else {
return Promise.reject(
new Error("[TINY Error][User]] Prop service is mandatory when the framework service is not used")
);
}
};
const $service = service || {};
const { common = {}, setting = {}, base = {} } = $service;
const { fetchUser, fetchW3Accounts, fetchSuggestUser, fetchUserByUserId } = $service;
const { options = {} } = setting;
const {
UserCache = { uuid: "uuid", userId: "userId", userCN: "userCN", dept: "dept", eno: "employeeNumber" },
User = { batch: 50, textField: "", valueField: "" }
} = options;
return {
fetchUser: props.fetchUser || fetchUser || noopFnCreator("fetchUser"),
fetchW3Accounts: props.fetchW3Accounts || fetchW3Accounts || noopFnCreator("fetchW3Accounts"),
fetchSuggestUser: props.fetchSuggestUser || fetchSuggestUser || noopFnCreator("fetchSuggestUser"),
fetchUserByUserId: props.fetchUserByUserId || fetchUserByUserId || noopFnCreator("fetchUserByUserId"),
getUserImageUrl: props.getUserImageUrl || common.getUserImageUrl || noopFnCreator("getUserImageUrl"),
getLangData: props.getLangData || base.getLangData || noopFnCreator("getLangData"),
getUserInfo: props.getUserInfo || base.getUserInfo || noopFnCreator("getUserInfo"),
userCache: props.UserCache || UserCache,
batch: User.batch,
textField: User.textField,
valueField: User.valueField
};
};
const filter = ({ props, state }) => () => {
if (props.multiple && props.hideSelected) {
const selectedUsers = state.user.map((value) => typeof value === "string" ? value.toLocaleLowerCase() : value);
return state.options.map((user) => {
const _show = !~selectedUsers.indexOf(
typeof user[state.valueField] === "string" ? user[state.valueField].toLocaleLowerCase() : user[state.valueField]
);
return __spreadProps(__spreadValues({}, user), {
_show
});
});
}
return state.options;
};
const computedTextField = ({ service, props }) => () => props.textField || service.textField || "userCN";
const computedValueField = ({ service, props }) => () => props.valueField || service.valueField || "userId";
export {
autoSelect,
cacheUser,
computedTextField,
computedValueField,
filter,
getUsers,
handleBlur,
initService,
initUser,
saveCache,
searchMethod,
setSelected,
suggestUser,
syncCacheIds,
updateCache,
updateOptions,
useSortable,
userChange,
visibleChange
};