New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

admate

Package Overview
Dependencies
Maintainers
0
Versions
67
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

admate - npm Package Compare versions

Comparing version 1.0.0-beta.2 to 1.0.0-beta.3

dist/admate.d.mts

585

dist/admate.d.ts

@@ -1,1 +0,584 @@

export { }
import { cloneDeep, debounce, isPlainObject, at, set, merge, assignIn } from 'lodash-es';
import { ref, reactive, watch, onMounted, isVue3 } from 'vue-demi';
import { conclude } from 'vue-global-config';
const METHODS_WITH_REQUEST_BODY = ['PUT', 'POST', 'DELETE', 'PATCH'];
const createURL = (urlPrefix, url) =>
urlPrefix
? url
? url.startsWith('/')
? url
: (urlPrefix.endsWith('/')
? urlPrefix
: `${urlPrefix}/`)
+ url
: urlPrefix
: url;
function configToCaller(axios, urlPrefix, config) {
// 如果配置非函数,则不需要每次调用接口都重新计算 URL
let url;
if (!(typeof config === 'function')) {
url = createURL(urlPrefix, config.url);
}
return config
? (payload, payloadAs) => {
let configComputed = config;
if (typeof config === 'function') {
configComputed = config(payload);
url = createURL(urlPrefix, configComputed.url);
}
if (payload) {
payloadAs ??= METHODS_WITH_REQUEST_BODY.includes(configComputed.method?.toUpperCase() || '')
? 'data'
: 'params';
}
return axios({
...payload && {
...(payloadAs === 'data' && { data: payload }),
...(payloadAs === 'params' && { params: payload }),
},
...configComputed,
url,
})
}
: () => {}
}
function createAPI(axios, axiosConfig) {
// cancelAllRequest() // 嵌套使用 Admate 时,可能导致将父级的请求取消掉
// source = CancelToken.source()
const api = {
list: {
read: configToCaller(axios, axiosConfig.urlPrefix, axiosConfig.list.read),
},
form: {},
};
for (const k in axiosConfig.form) {
api.form[k] = configToCaller(axios, axiosConfig.urlPrefix, axiosConfig.form[k]);
}
return api
}
/* export const cancelAllRequest = () => {
// 即使不存在 pending 的请求 cancel() 也会触发 axios.interceptors.response.use.onRejected
if (source) {
source.cancel()
source = undefined
}
} */
function getValue(object, path) {
if (object && path) {
switch (typeof path) {
case 'string':
// paths 为 undefined 或 '' 时结果为 undefined
return at(object, path)[0]
case 'function':
return path(object)
case 'symbol':
if (isPlainObject(object)) {
return object[path]
}
}
}
return object
}
function setValue(object, path, value) {
if (object && path) {
switch (typeof path) {
case 'string':
// paths 为 undefined 或 '' 时结果为 undefined
// set 会改变原始对象
return set(object, path, value)
case 'function':
return path(object)
case 'symbol':
if (isPlainObject(object)) {
object[path] = value;
}
}
}
return object
}
// 将接口返回值混入form.data
function mergeFormData(formExported, newFormData) {
if (formExported.mergeData && isPlainObject(formExported.data) && isPlainObject(newFormData)) {
// if (isProxy(formExported.data)) { // vue 2 中报错
if (isVue3) {
// merge, assignIn 会改变原始对象
// merge, assignIn 会改变原始对象
if (formExported.mergeData === 'deep') {
merge(formExported.data, newFormData);
}
else if (formExported.mergeData === 'shallow') {
assignIn(formExported.data, newFormData);
}
else if (typeof formExported.mergeData === 'function') {
formExported.mergeData(newFormData);
}
}
else {
// merge, assignIn, Object.assign 对对象属性的修改在 vue 2中无法触发更新
// https://cn.vuejs.org/v2/guide/reactivity.html#%E5%AF%B9%E4%BA%8E%E5%AF%B9%E8%B1%A1
// 可选择直接赋值,或者 Vue.set
if (formExported.mergeData === 'deep') {
formExported.data = merge(cloneDeep(formExported.data), newFormData);
}
else if (formExported.mergeData === 'shallow') {
formExported.data = {
...formExported.data,
...newFormData,
};
}
else if (typeof formExported.mergeData === 'function') {
formExported.mergeData(newFormData);
}
}
}
else {
formExported.data = newFormData;
}
}
function useAdmate({
axios,
axiosConfig,
list,
form,
}) {
const api = createAPI(axios, axiosConfig);
const readListTrigger = ref();
const getInitialList = () =>
conclude([list], {
default: userProp => ({
data: [],
loading: false,
total: 0,
filter: setValue({}, userProp?.pageNumberAt, 1),
watchFilter: true,
debounce: 300,
proxy: {},
}),
defaultIsDynamic: true,
});
const initialList = getInitialList();
const initialListFilter = cloneDeep(initialList.filter);
const listExported = reactive(initialList);
const getInitialForm = () =>
cloneDeep({
status: '',
show: false,
// closeDelay: 500,
// closed: true,
data: {},
mergeData: 'deep',
loading: false,
submitting: false,
title: '', // throw TypeError: 'set' on proxy: trap returned falsish for property 'title' when closing form, see https://github.com/vuejs/core/issues/12138
proxy: {},
...form,
});
const formExported = reactive(getInitialForm());
// 设置表单的终态
const setTerminalFormState = ({
// todo: vue 2 中传参后再修改会丢失响应性
// target,
state,
defaultState,
mergeState = 'shallow',
}) => {
const TERMINAL_STATE = conclude([state, defaultState]);
// merge, assignIn, Object.assign 对对象属性的修改在 vue 2中无法触发更新
// 但是对于对象本身是可以生效的,且直接赋值反而无效
if (mergeState === 'deep') {
merge(formExported, TERMINAL_STATE);
}
else {
assignIn(formExported, TERMINAL_STATE);
}
};
const readList = (payload = listExported.filter, payloadAs) => {
listExported.loading = true;
return api.list.read(payload, payloadAs)
.then((response) => {
listExported.data = getValue(response, listExported.dataAt) ?? [];
listExported.total = listExported.data?.length ? getValue(response, listExported.totalAt) ?? 0 : 0;
return response
})
.catch(() => {
// listExported.data.length = 0 // listExported.data 可能为空
listExported.data = [];
})
.finally(() => {
listExported.loading = false;
})
};
const resetList = (payload, payloadAs) => {
listExported.filter = cloneDeep(initialListFilter);
if (!listExported.watchFilter) {
readList(listExported.filter, payloadAs);
}
};
let oldPageNumber = 1;
listExported.read = (...args) => {
const newPageNumber = getValue(listExported.filter, listExported.pageNumberAt);
if (readListTrigger.value === 'filterChange' && newPageNumber !== 1) {
// 如果改变的不是页码,页码重置为1,并拦截本次请求
setValue(listExported.filter, listExported.pageNumberAt, 1);
readListTrigger.value = undefined;
return
}
oldPageNumber = newPageNumber;
// args 是用户直接调用 list.read 传的参,优先级低
// argsProxy 是用户在 list.proxy.read 内部调用 readList 传的参,优先级高
const result = listExported.proxy.read
? listExported.proxy.read(
(...argsProxy) => readList(...(argsProxy.length ? argsProxy : args)),
readListTrigger.value,
)
: readList(...args);
readListTrigger.value = undefined;
return result
/* if (result instanceof Promise) {
// 不能统一写在finally中,因为:
// 在then中能拿到用户resolve或者在then中return的参数
// 在catch中能拿到用户reject或者在catch中return的参数
// 在finally中拿不到参数
result.then((state?: List) => {
setTerminalFormState({
target: listExported,
state,
defaultState: {
loading: false
}
})
}).catch((state?: List) => {
setTerminalFormState({
target: listExported,
state,
defaultState: {
loading: false
}
})
})
} else {
setTerminalFormState({
target: listExported,
state: result,
defaultState: {
loading: false
}
})
} */
};
// 列表重置
listExported.reset = (...args) => {
// args 是用户直接调用 list.reset 传的参,优先级低
// argsProxy 是用户在 list.proxy.reset 内部调用 resetList 传的参,优先级高
const result = listExported.proxy.reset
? listExported.proxy.reset(
(...argsProxy) => resetList(...(argsProxy.length ? argsProxy : args)),
)
: resetList(...args);
readListTrigger.value = undefined;
return result
};
// 列表筛选,页码重置
listExported.search = (...args) => {
if (getValue(listExported.filter, listExported.pageNumberAt) === 1) {
listExported.read(...args);
}
else {
setValue(listExported.filter, listExported.pageNumberAt, 1);
if (!listExported.watchFilter) {
listExported.read(...args);
}
}
};
// 删除单条记录
formExported.delete = (payload, payloadAs) =>
api.form.delete(payload, payloadAs).then((response) => {
if (listExported.data?.length === 1) {
const currPageNumber = getValue(listExported.filter, listExported.pageNumberAt);
if (currPageNumber === 1) {
readListTrigger.value = 'delete';
listExported.read();
}
else {
readListTrigger.value = 'delete';
setValue(listExported.filter, listExported.pageNumberAt, currPageNumber - 1);
if (!listExported.watchFilter) {
listExported.read();
}
}
}
else {
readListTrigger.value = 'delete';
listExported.read();
}
return response
});
// 改变单条记录状态
formExported.switch = (payload, payloadAs) =>
api.form.switch(payload, payloadAs).then((response) => {
readListTrigger.value = 'switch';
listExported.read();
return response
});
// 坑:
/*
let obj = { a: 1 }
obj = {
...obj,
...(obj => { // 该方法中对obj的修改不会对上一行中的obj生效
obj.x = 1
})()
}
*/
const openForm = (payload, payloadAs) => {
// 查看和编辑时,回显单条记录数据
if (payload) {
if (payloadAs === 'cache') {
mergeFormData(formExported, cloneDeep(payload));
formExported.show = true;
}
else {
formExported.loading = true;
formExported.show = true;
return api.form.read(payload, payloadAs).then((response) => {
mergeFormData(formExported, getValue(response, formExported.dataAt));
return response
})
}
}
else {
// 查看时参数必传,编辑时可以不传因为可能是覆盖式编辑
if (formExported.status === 'read' && !arguments.length) {
console.warn('When the form status is \'read\', the parameter of `form.open` must be passed');
}
formExported.show = true;
}
};
formExported.open = (...args) => {
const result = form.proxy.open
? form.proxy.open((...argsProxy) =>
// args 是用户直接调用 form.open 传的参,优先级低
// argsProxy 是用户在 form.proxy.open 内部调用 openForm 传的参,优先级高
openForm(...(argsProxy.length ? argsProxy : args)),
)
: openForm(...args);
if (result instanceof Promise) {
result
.then((state) => {
setTerminalFormState({
target: formExported,
state,
defaultState: {
loading: false,
},
});
})
.catch((state) => {
setTerminalFormState({
target: formExported,
state,
defaultState: {
show: false,
},
});
});
}
else {
setTerminalFormState({
target: formExported,
state: result,
defaultState: {
loading: false,
},
});
}
return result
};
formExported.create = (...args) => {
formExported.status = 'create';
formExported.open(...args);
};
formExported.read = (...args) => {
formExported.status = 'read';
formExported.open(...args);
};
formExported.update = (...args) => {
formExported.status = 'update';
formExported.open(...args);
};
// 表单提交
const submitForm = (payload = formExported.data, payloadAs) => {
if (!formExported.status || !['create', 'update'].includes(formExported.status)) {
throw new Error('submitForm can only be called when the form status is \'create\' or \'update\'')
}
formExported.submitting = true;
return api.form[formExported.status](payload, payloadAs).then((response) => {
readListTrigger.value = formExported.status;
listExported.read();
return response
})
};
formExported.submit = (...args) => {
const result = form.proxy.submit
? form.proxy.submit((...argsProxy) =>
// params 是用户直接调用 form.submit 传的参,优先级低
// args 是用户在 form.proxy.submit 内部调用 submitForm 传的参,优先级高
submitForm(...(argsProxy.length ? argsProxy : args)),
)
: submitForm(...args);
if (result instanceof Promise) {
result
.then((state) => {
setTerminalFormState({
target: formExported,
state,
defaultState: {
show: false,
},
});
})
.catch((state) => {
setTerminalFormState({
target: formExported,
state,
defaultState: {
submitting: false,
},
});
});
}
else {
setTerminalFormState({
target: formExported,
state: result,
defaultState: {
show: false,
},
});
}
return result
};
watch(
() => formExported.show,
(newShow) => {
if (!newShow) {
// 表单关闭时,重置表单
// 可能会有关闭动画,所以加延迟
setTimeout(() => {
Object.assign(formExported, {
...getInitialForm(),
// 不能重置被监听的 show
// 因为重置是异步的,如果在此 500ms 期间 show 被外部赋为 true,将导致死循环
show: formExported.show,
});
}, 500);
}
},
);
// 重置所有数据
/* const destroy = () => {
readListDebounced.value = null
Object.assign(listExported, getInitialList())
Object.assign(formExported, getInitialForm())
} */
// 首次获取列表
readListTrigger.value = 'immediate';
listExported.read();
onMounted(() => {
// 筛选项改变时,刷新列表
if (listExported.watchFilter) {
const readListDebounced = ref(
debounce(() => {
readListTrigger.value = 'filterChange';
listExported.read();
}, listExported.debounce),
);
// 异步的目的:避免 onMounted 时给 list.filter 赋初值触发 watch,该 watch 应仅由用户操作触发
setTimeout(() => {
watch(
() => listExported.filter,
() => {
if (getValue(listExported.filter, listExported.pageNumberAt) === oldPageNumber) {
readListDebounced.value();
}
else {
// 翻页不需要防抖
// ??= 的原因是删除当前分页最后一条记录时也会触发翻页
readListTrigger.value ??= 'pageNumberChange';
listExported.read();
}
},
{
deep: true,
},
);
}, 0);
}
});
/* onUnmounted(() => {
/!**
* 页面销毁时如果还有查询请求,中止掉
* 不能在 onUnmounted 调用 cancelAllRequest,
* 因为下一个页面的 setup 会早于上一个页面的 onUnmounted 执行,
* 导致中止掉下一个页面的请求
*!/
//cancelAllRequest()
//destroy()
}) */
return { list: listExported, form: formExported }
}
export = useAdmate;

51

package.json
{
"name": "admate",
"PascalCasedName": "Admate",
"version": "1.0.0-beta.2",
"type": "commonjs",
"version": "1.0.0-beta.3",
"private": false,

@@ -12,3 +13,6 @@ "description": "Admin CRUD frontend meta framework for Vue 2/3, extremely concise without losing flexibility.",

"license": "MIT",
"repository": "https://github.com/cloydlau/admate",
"repository": {
"type": "git",
"url": "git+https://github.com/cloydlau/admate.git"
},
"keywords": [

@@ -38,5 +42,10 @@ "admin",

".": {
"types": "./dist/admate.d.ts",
"import": "./dist/admate.mjs",
"require": "./dist/admate.umd.js"
"import": {
"types": "./dist/admate.d.mts",
"default": "./dist/admate.mjs"
},
"require": {
"types": "./dist/admate.d.ts",
"default": "./dist/admate.umd.js"
}
},

@@ -56,7 +65,9 @@ "./*": "./*"

"dev": "esno ./scripts/dev.mts",
"build": "vite build",
"doc": "vitepress dev --open /README",
"typegen": "npx tsup --entry.admate src/index.js --format esm,cjs --cjsInterop --clean --dts-only",
"build": "vite build && pnpm typegen",
"doc": "vitepress dev --open /RsEADME",
"serve": "vite preview",
"release": "esno ./scripts/release.mts",
"license-scan": "license-checker --summary --out ./licenses.txt",
"sync-to-cnpm": "npx cnpm sync && curl -L https://npmmirror.com/sync/admate",
"license-scan": "license-checker --summary --out ./depencency-licenses.txt",
"lint": "eslint \"**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,json,md,html,css,scss,sass}\" --ignore-pattern stats.html",

@@ -80,3 +91,4 @@ "lint:fix": "eslint \"**/*.{js,mjs,cjs,jsx,ts,mts,cts,tsx,vue,json,md,html,css,scss,sass}\" --ignore-pattern stats.html --fix",

"devDependencies": {
"@antfu/eslint-config": "^3.7.1",
"@antfu/eslint-config": "^3.7.3",
"@arethetypeswrong/cli": "^0.16.4",
"@commitlint/cli": "^19.5.0",

@@ -86,3 +98,3 @@ "@commitlint/config-conventional": "^19.5.0",

"@types/lodash-es": "^4.17.12",
"@types/node": "^22.7.0",
"@types/node": "^22.7.5",
"@vitejs/plugin-vue": "latest",

@@ -94,8 +106,9 @@ "@vue/compiler-sfc": "latest",

"case-police": "^0.7.0",
"cnpm": "^9.4.0",
"cross-spawn": "^7.0.3",
"del": "^7.1.0",
"del": "^8.0.0",
"element-plus": "latest",
"eslint-plugin-format": "^0.1.2",
"esno": "^4.7.0",
"faim": "^0.9.7",
"esno": "^4.8.0",
"faim": "^0.10.0",
"kolorist": "^1.8.0",

@@ -110,10 +123,11 @@ "license-checker": "^25.0.1",

"prompts": "^2.4.2",
"publint": "^0.2.11",
"qs": "^6.13.0",
"react": "^18.3.1",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.79.3",
"sass": "^1.79.5",
"semver": "^7.6.3",
"simple-git-hooks": "^2.11.1",
"typescript": "^5.6.2",
"unocss": "~0.62.4",
"tsup": "^8.3.0",
"typescript": "^5.6.3",
"unocss": "~0.63.4",
"unplugin-auto-import": "^0.18.3",

@@ -123,5 +137,4 @@ "unplugin-unused": "^0.2.3",

"vite": "^5.4.8",
"vite-plugin-dts": "4.2.2",
"vite-plugin-mock": "^3.0.2",
"vitepress": "^1.3.4",
"vitepress": "^1.4.0",
"vue": "latest",

@@ -128,0 +141,0 @@ "vue-global-config": "^0.6.2",

@@ -28,3 +28,3 @@ <h1 align="center">

- 🖖 **Vue 2.6/2.7/3 一体通用** - 零成本升级
- 🤸 **跨平台** - 解耦合 UI 框架,极低成本接入任何 Vue 生态中后台模板/框架如 <a href="https://github.com/pure-admin/vue-pure-admin">vue-pure-admin</a>,<a href="https://github.com/vbenjs/vue-vben-admin">vue-vben-admin</a> 或 <a href="https://github.com/yudaocode/yudao-ui-admin-vue3">yudao-ui-admin-vue3</a>
- 🤸 **跨框架** - 解耦合 UI 框架,极低成本接入任何 Vue 生态中后台模板/框架如 <a href="https://github.com/pure-admin/vue-pure-admin">vue-pure-admin</a>,<a href="https://github.com/vbenjs/vue-vben-admin">vue-vben-admin</a> 或 <a href="https://github.com/yudaocode/yudao-ui-admin-vue3">yudao-ui-admin-vue3</a>
- 🌐 **规整统一的页面代码风格** - 避免各个页面的代码风格五花八门,提升可读性、降低维护成本

@@ -31,0 +31,0 @@ - 🥥 **模块级别的请求配置** - 虽然 Axios 支持全局配置,由于同模块内请求配置相似,接口前缀通常是一致的,所以往往还需要模块级别的配置

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc