forked from mengyxu/noob-components
26 changed files with 1581 additions and 491 deletions
@ -0,0 +1,102 @@ |
|||||||
|
<template> |
||||||
|
<el-container> |
||||||
|
<el-main> |
||||||
|
<ListTable |
||||||
|
page |
||||||
|
border |
||||||
|
:data="tableData.rows" |
||||||
|
:props="propsWithSelection" |
||||||
|
:row-key="rowKey" |
||||||
|
:example="tableData.example" |
||||||
|
@query="tableData.query()" |
||||||
|
> |
||||||
|
<template v-for="{ code } in propsWithSlot" #[code]="{ row }"> |
||||||
|
<slot :name="code" :row /> |
||||||
|
</template> |
||||||
|
<template #ui_selection="{ row }"> |
||||||
|
<el-checkbox |
||||||
|
:value="selection.get(row[rowKey])" |
||||||
|
@update:model-value=" |
||||||
|
(val) => { |
||||||
|
if (val) { |
||||||
|
selection.set(row[rowKey], row); |
||||||
|
} else { |
||||||
|
selection.delete(row[rowKey]); |
||||||
|
} |
||||||
|
} |
||||||
|
" |
||||||
|
/> |
||||||
|
</template> |
||||||
|
</ListTable> |
||||||
|
</el-main> |
||||||
|
<el-footer> |
||||||
|
<ConfirmCancel @confirm="handleConfirm" @cancel="handleCancel" /> |
||||||
|
</el-footer> |
||||||
|
</el-container> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { ListTable, Element, AsyncHandler, useAsyncEmits, useListTable, useSysDict, ConfirmCancel } from "noob-mengyxu"; |
||||||
|
import { computed, onMounted, reactive, ref } from "vue"; |
||||||
|
import { useStore } from "vuex"; |
||||||
|
import { useI18n } from "vue3-i18n"; |
||||||
|
import { ListTableProps } from "../../../plugs/element/listTableDialog"; |
||||||
|
import { PageResponse } from "../../../plugs/http"; |
||||||
|
const { showMessage } = Element; |
||||||
|
const { t } = useI18n(); |
||||||
|
|
||||||
|
defineOptions({ inheritAttrs: false }); |
||||||
|
|
||||||
|
type Row = Record<string, any>; |
||||||
|
|
||||||
|
const p = defineProps<ListTableProps>(); |
||||||
|
|
||||||
|
type Emits = { |
||||||
|
query: [handler: AsyncHandler<PageResponse<Row>>, example: Record<string, any>]; |
||||||
|
confirm: [handler: AsyncHandler<void>, rows: Row[]]; |
||||||
|
close: [handler: AsyncHandler<void>]; |
||||||
|
}; |
||||||
|
const emits = defineEmits<Emits>(); |
||||||
|
const emitsAsync = useAsyncEmits<Emits>(emits); |
||||||
|
|
||||||
|
const propsWithSelection = computed(() => [ |
||||||
|
{ code: "ui_selection", slot: true, i18n: "common.select", width: 80 }, |
||||||
|
...p.props, |
||||||
|
]); |
||||||
|
const propsWithSlot = computed(() => p.props.filter((col) => col.slot)); |
||||||
|
|
||||||
|
const store = useStore(); |
||||||
|
|
||||||
|
const { sysDict, updateDict } = useSysDict(); |
||||||
|
|
||||||
|
const tableData = useListTable({ |
||||||
|
query: (example) => emitsAsync("query", example), |
||||||
|
initialPage: p.initialPage, |
||||||
|
initialPageSize: p.initialPageSize, |
||||||
|
initExample: p.initExample, |
||||||
|
disableAutoQuery: true, |
||||||
|
}); |
||||||
|
|
||||||
|
const selection = reactive(new Map()); |
||||||
|
|
||||||
|
const handleConfirm = async () => { |
||||||
|
try { |
||||||
|
await emitsAsync("confirm", Array.from(selection.values())); |
||||||
|
await emitsAsync("close"); |
||||||
|
} catch { |
||||||
|
showMessage("error", t("common.errors.submitError")); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const handleCancel = async () => { |
||||||
|
await emitsAsync("close"); |
||||||
|
}; |
||||||
|
|
||||||
|
onMounted(async () => { |
||||||
|
if (p.useDicts) { |
||||||
|
await updateDict(p.useDicts); |
||||||
|
} |
||||||
|
|
||||||
|
await tableData.query(); |
||||||
|
}); |
||||||
|
</script> |
||||||
@ -1,16 +1,32 @@ |
|||||||
import NoobTag from './item/tag.vue'; |
import NoobTag from "./item/tag.vue"; |
||||||
import NoobButton from './item/button.vue'; |
import NoobButton from "./item/button.vue"; |
||||||
import NoobSelect from './item/select.vue'; |
import NoobSelect from "./item/select.vue"; |
||||||
import NoobInput from './item/input.vue'; |
import NoobInput from "./item/input.vue"; |
||||||
import NoobDate from './item/datetime.vue'; |
import NoobDate from "./item/datetime.vue"; |
||||||
import LightBox from './item/light-box.vue'; |
import LightBox from "./item/light-box.vue"; |
||||||
|
import ButtonWithTooltip from "./item/buttonWithTooltip.vue"; |
||||||
|
import ConfirmCancel from "./item/confirmCancel.vue"; |
||||||
|
import TzDatePicker from "./item/tzDatePicker.vue"; |
||||||
|
import TzDateTime from "./item/tzDateTime.vue"; |
||||||
|
import SearchRow from "./data/search-row.vue"; |
||||||
|
import ListTable from "./data/list-table.vue"; |
||||||
|
import Infomation from "./data/infomation.vue"; |
||||||
|
import ModifyForm from "./data/modify-form.vue"; |
||||||
|
import Descriptions from "./data/descriptions.vue"; |
||||||
|
import TableAction from "./data/table-action.vue"; |
||||||
|
import ListTableDialog from "./data/listTableDialog.vue"; |
||||||
|
|
||||||
export { NoobTag, NoobButton, NoobSelect, NoobInput, NoobDate, LightBox }; |
export { |
||||||
|
NoobTag, |
||||||
|
NoobButton, |
||||||
|
NoobSelect, |
||||||
|
NoobInput, |
||||||
|
NoobDate, |
||||||
|
LightBox, |
||||||
|
ButtonWithTooltip, |
||||||
|
TzDateTime, |
||||||
|
TzDatePicker, |
||||||
|
ConfirmCancel, |
||||||
|
}; |
||||||
|
|
||||||
import SearchRow from './data/search-row.vue'; |
export { SearchRow, ListTable, ListTableDialog, Infomation, ModifyForm, Descriptions, TableAction }; |
||||||
import ListTable from './data/list-table.vue'; |
|
||||||
import Infomation from './data/infomation.vue'; |
|
||||||
import ModifyForm from './data/modify-form.vue'; |
|
||||||
import Descriptions from './data/descriptions.vue'; |
|
||||||
import TableAction from './data/table-action.vue'; |
|
||||||
export { SearchRow, ListTable, Infomation, ModifyForm, Descriptions, TableAction }; |
|
||||||
|
|||||||
@ -0,0 +1,46 @@ |
|||||||
|
<template> |
||||||
|
<el-tooltip |
||||||
|
v-if="props.tooltip" |
||||||
|
:content="props.tooltip" |
||||||
|
:disabled="props.disabled" |
||||||
|
:show-after="0" |
||||||
|
:hide-after="0" |
||||||
|
:popper-options="{ modifiers: [{ name: 'eventListeners', enabled: false }] }" |
||||||
|
popper-class="non-interactive-tooltip" |
||||||
|
> |
||||||
|
<NoobButton :disabled="props.disabled" v-bind="buttonProps"> |
||||||
|
<slot /> |
||||||
|
</NoobButton> |
||||||
|
</el-tooltip> |
||||||
|
<NoobButton v-else :disabled="props.disabled" v-bind="buttonProps"> |
||||||
|
<slot /> |
||||||
|
</NoobButton> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { useAttrs } from "vue"; |
||||||
|
import { ElTooltip } from "element-plus"; |
||||||
|
import { computed } from "vue"; |
||||||
|
import { NoobButton } from "noob-mengyxu"; |
||||||
|
|
||||||
|
defineOptions({ inheritAttrs: false }); |
||||||
|
|
||||||
|
interface Props { |
||||||
|
tooltip?: string; |
||||||
|
disabled: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
const props = defineProps<Props>(); |
||||||
|
const attrs = useAttrs(); |
||||||
|
|
||||||
|
const buttonProps = computed(() => { |
||||||
|
const { class: _, ...rest } = attrs; |
||||||
|
return rest; |
||||||
|
}); |
||||||
|
</script> |
||||||
|
|
||||||
|
<style> |
||||||
|
.non-interactive-tooltip { |
||||||
|
pointer-events: none !important; |
||||||
|
} |
||||||
|
</style> |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
<template> |
||||||
|
<el-row> |
||||||
|
<NoobButton type="primary" @click="emit('confirm')">{{ |
||||||
|
t("base.confirm") |
||||||
|
}}</NoobButton> |
||||||
|
<el-space fill /> |
||||||
|
<NoobButton type="info" @click="emit('cancel')">{{ |
||||||
|
t("base.cancel") |
||||||
|
}}</NoobButton> |
||||||
|
</el-row> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { NoobButton } from "noob-mengyxu"; |
||||||
|
import { useI18n } from "vue3-i18n"; |
||||||
|
const { t } = useI18n(); |
||||||
|
|
||||||
|
const emit = defineEmits<{ |
||||||
|
(e: "confirm"): void | Promise<void>; |
||||||
|
(e: "cancel"): void | Promise<void>; |
||||||
|
}>(); |
||||||
|
</script> |
||||||
@ -1,73 +1,87 @@ |
|||||||
<template> |
<template> |
||||||
<el-date-picker :size="state.size.size" :class="['form-item', full && 'full']" :value-format="formater" |
<el-date-picker |
||||||
v-model="myValue" :type="type" :placeholder="placeholder || t('rule.pleaseEnter')" :disabled="disabled" |
:size="state.size.size" |
||||||
:clearable="clearable" :teleported="false" /> |
:class="['form-item', full && 'full']" |
||||||
|
:value-format="formater" |
||||||
|
v-model="myValue" |
||||||
|
:type="type" |
||||||
|
:placeholder="placeholder || t('rule.pleaseEnter')" |
||||||
|
:disabled="disabled" |
||||||
|
:clearable="clearable" |
||||||
|
:teleported="false" |
||||||
|
/> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||||
import { useStore } from "vuex"; |
import { useStore } from "vuex"; |
||||||
import { onMounted, ref, watch } from "vue"; |
import { onMounted, ref, watch } from "vue"; |
||||||
import { useI18n } from 'vue3-i18n'; |
import { useI18n } from "vue3-i18n"; |
||||||
const { t } = useI18n(); |
const { t } = useI18n(); |
||||||
const { state } = useStore(); |
const { state } = useStore(); |
||||||
|
|
||||||
const prop = defineProps({ |
const prop = defineProps({ |
||||||
modelValue: null, |
modelValue: null, |
||||||
placeholder: { |
placeholder: { |
||||||
type: String, |
type: String, |
||||||
default: null |
default: null, |
||||||
}, |
}, |
||||||
type: { |
type: { |
||||||
type: String, |
type: String, |
||||||
default: 'datetime' |
default: "datetime", |
||||||
}, |
}, |
||||||
disabled: { |
disabled: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: false, |
default: false, |
||||||
}, |
}, |
||||||
clearable: { |
clearable: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
full: { |
full: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: false, |
default: false, |
||||||
}, |
}, |
||||||
formater: { |
formater: { |
||||||
type: String, |
type: String, |
||||||
default: 'YYYY-MM-DD HH:mm:ss' |
default: "YYYY-MM-DD HH:mm:ss", |
||||||
}, |
}, |
||||||
width: { |
width: { |
||||||
type: Number |
type: Number, |
||||||
} |
}, |
||||||
}); |
}); |
||||||
const emit = defineEmits(["update:modelValue"]); |
const emit = defineEmits(["update:modelValue"]); |
||||||
const myValue = ref(null); |
const myValue = ref(null); |
||||||
const width = ref('150px'); |
const width = ref("150px"); |
||||||
const setWidth = () => { |
const setWidth = () => { |
||||||
if (prop.width) { |
if (prop.width) { |
||||||
width.value = prop.width + 'px'; |
width.value = prop.width + "px"; |
||||||
} else { |
} else { |
||||||
width.value = state.size.searchWidth; |
width.value = state.size.searchWidth; |
||||||
} |
} |
||||||
} |
}; |
||||||
watch(() => state.size, (n, o) => { |
watch( |
||||||
|
() => state.size, |
||||||
|
(n, o) => { |
||||||
setWidth(); |
setWidth(); |
||||||
}) |
} |
||||||
|
); |
||||||
watch(myValue, (n, o) => { |
watch(myValue, (n, o) => { |
||||||
emit('update:modelValue', n); |
emit("update:modelValue", n); |
||||||
}) |
}); |
||||||
watch(() => prop.modelValue, (n, o) => { |
watch( |
||||||
|
() => prop.modelValue, |
||||||
|
(n, o) => { |
||||||
myValue.value = n; |
myValue.value = n; |
||||||
}) |
} |
||||||
|
); |
||||||
onMounted(() => { |
onMounted(() => { |
||||||
prop.modelValue && (myValue.value = prop.modelValue); |
prop.modelValue && (myValue.value = prop.modelValue); |
||||||
setWidth(); |
setWidth(); |
||||||
}); |
}); |
||||||
</script> |
</script> |
||||||
<style lang="scss"> |
<style lang="scss"> |
||||||
//@import url(); 引入公共css类 |
//@import url(); 引入公共css类 |
||||||
.form-item { |
.form-item { |
||||||
width: v-bind('width'); |
width: v-bind("width"); |
||||||
} |
} |
||||||
</style> |
</style> |
||||||
@ -0,0 +1,54 @@ |
|||||||
|
<template> |
||||||
|
<el-date-picker v-model="model"> </el-date-picker> |
||||||
|
</template> |
||||||
|
|
||||||
|
<script setup lang="ts"> |
||||||
|
import { ElDatePicker } from "element-plus"; |
||||||
|
import dayjs, { type Dayjs } from "dayjs"; |
||||||
|
import { nextTick, toRef, watch, watchEffect } from "vue"; |
||||||
|
import { watchEffectOnce } from "../../../plugs/composables"; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
valueType: "iso8601" | "unix" | "unixMillis"; |
||||||
|
tz?: string; |
||||||
|
keepMillis?: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
const props = defineProps<Props>(); |
||||||
|
|
||||||
|
watchEffectOnce(toRef(props, "tz"), async () => { |
||||||
|
const timeZone = await import("dayjs/plugin/timezone"); |
||||||
|
dayjs.extend(timeZone.default); |
||||||
|
}); |
||||||
|
|
||||||
|
const model = defineModel<string | number, string, Date, Date>({ |
||||||
|
get: (value) => { |
||||||
|
let dt: Dayjs; |
||||||
|
if (props.valueType === "unix" || props.valueType === "unixMillis") { |
||||||
|
if (typeof value === "string") { |
||||||
|
value = parseFloat(value); |
||||||
|
} |
||||||
|
dt = props.valueType === "unix" ? dayjs.unix(value) : dayjs(value); |
||||||
|
} else { |
||||||
|
dt = dayjs(value); |
||||||
|
} |
||||||
|
|
||||||
|
return dt.toDate(); |
||||||
|
}, |
||||||
|
set: (value) => { |
||||||
|
const dt = dayjs(value); |
||||||
|
if (props.valueType === "unix") { |
||||||
|
return dt.unix(); |
||||||
|
} else if (props.valueType === "unixMillis") { |
||||||
|
return dt.valueOf(); |
||||||
|
} else if (props.valueType === "iso8601" && props.tz) { |
||||||
|
const fmt = props.keepMillis ? "YYYY-MM-DDTHH:mm:ss.SSSZ" : "YYYY-MM-DDTHH:mm:ssZ"; |
||||||
|
return dt.tz(props.tz).format(fmt); |
||||||
|
} else { |
||||||
|
const isoWithMillis = dt.toISOString(); |
||||||
|
if (props.keepMillis) return isoWithMillis; |
||||||
|
else return isoWithMillis.slice(0, 19).concat("Z"); |
||||||
|
} |
||||||
|
}, |
||||||
|
}); |
||||||
|
</script> |
||||||
@ -0,0 +1,55 @@ |
|||||||
|
<template> |
||||||
|
{{ display }} |
||||||
|
</template> |
||||||
|
<script setup lang="ts"> |
||||||
|
import dayjs from "dayjs"; |
||||||
|
import { computed, toRef } from "vue"; |
||||||
|
import { watchEffectOnce } from "noob-mengyxu"; |
||||||
|
|
||||||
|
interface Props { |
||||||
|
value: string | number; |
||||||
|
valueFormat?: string; |
||||||
|
valueTz?: string; |
||||||
|
displayFormat?: string; |
||||||
|
locale?: string; |
||||||
|
type?: "iso8601" | "unix" | "unixMillis"; |
||||||
|
} |
||||||
|
|
||||||
|
const props = defineProps<Props>(); |
||||||
|
|
||||||
|
watchEffectOnce(toRef(props, "valueFormat"), async () => { |
||||||
|
const customParseFormat = await import("dayjs/plugin/customParseFormat"); |
||||||
|
dayjs.extend(customParseFormat.default); |
||||||
|
}); |
||||||
|
|
||||||
|
watchEffectOnce(toRef(props, "valueTz"), async () => { |
||||||
|
const timeZone = await import("dayjs/plugin/timezone"); |
||||||
|
dayjs.extend(timeZone.default); |
||||||
|
}); |
||||||
|
|
||||||
|
const parsed = computed(() => { |
||||||
|
if (props.type === "unix" || props.type === "unixMillis") { |
||||||
|
const value = typeof props.value === "string" ? parseFloat(props.value) : props.value; |
||||||
|
return props.type === "unix" ? dayjs.unix(value) : dayjs(value); |
||||||
|
} else { |
||||||
|
if (props.valueFormat && props.valueTz) { |
||||||
|
return dayjs.tz(props.value, props.valueFormat, props.valueTz); |
||||||
|
} else if (props.valueTz) { |
||||||
|
return dayjs.tz(props.value, props.valueTz); |
||||||
|
} else if (props.valueFormat) { |
||||||
|
return dayjs(props.value, props.valueFormat); |
||||||
|
} else { |
||||||
|
return dayjs(props.value); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
const display = computed(() => { |
||||||
|
let dt = parsed.value; |
||||||
|
if (props.locale) { |
||||||
|
dt = dt.locale(props.locale); |
||||||
|
} |
||||||
|
|
||||||
|
return dt.format(props.displayFormat); |
||||||
|
}); |
||||||
|
</script> |
||||||
@ -1,247 +1,241 @@ |
|||||||
<template> |
<template> |
||||||
<el-container :style="appMain" ref="main" v-show="!flag.loading"> |
<el-container :style="appMain" ref="main" v-show="!flag.loading"> |
||||||
<el-header v-show="state.size.headHeight != '0px'" class="app-head" :height="state.size.headHeight"> |
<el-header v-show="state.size.headHeight != '0px'" class="app-head" :height="state.size.headHeight"> |
||||||
|
<Head :title="title" :logo="logo" :username="username"> |
||||||
<Head :title="title" :logo="logo" :username="username"> |
<template #left> |
||||||
<template #left> |
<MenuTree v-show="mode == 'horizontal'" :data="menus" mode="horizontal" /> |
||||||
<MenuTree v-show="mode == 'horizontal'" :data="menus" mode="horizontal" /> |
</template> |
||||||
</template> |
<HeadPersonal @updatePwd="updatePwd" @logout="onLogout" :center="center" /> |
||||||
<HeadPersonal @updatePwd="updatePwd" @logout="onLogout" :center="center" /> |
<Fullscreen /> |
||||||
<Fullscreen /> |
<StyleChange v-if="styleAble" /> |
||||||
<StyleChange v-if="styleAble" /> |
<LangChange v-if="langAble" /> |
||||||
<LangChange v-if="langAble" /> |
<SizeChange v-if="sizeAble" /> |
||||||
<SizeChange v-if="sizeAble" /> |
</Head> |
||||||
</Head> |
</el-header> |
||||||
|
<el-container id="container"> |
||||||
</el-header> |
<el-aside v-show="mode == 'vertical' && state.size.asideWidth != '0px'" :width="state.size.asideWidth"> |
||||||
<el-container id="container"> |
<MenuTree :data="menus" mode="vertical" /> |
||||||
<el-aside v-show="mode == 'vertical' && state.size.asideWidth != '0px'" :width="state.size.asideWidth"> |
</el-aside> |
||||||
<MenuTree :data="menus" mode="vertical" /> |
<el-main class="app-main"> |
||||||
</el-aside> |
<router-view /> |
||||||
<el-main class="app-main"> |
</el-main> |
||||||
<router-view /> |
|
||||||
</el-main> |
|
||||||
</el-container> |
|
||||||
</el-container> |
</el-container> |
||||||
|
</el-container> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||||
import { reactive, onMounted, ref, onBeforeUnmount } from "vue"; |
import { onBeforeUnmount, onMounted, reactive, ref } from "vue"; |
||||||
import { useStore } from "vuex"; |
import { useStore } from "vuex"; |
||||||
import { useRouter } from "vue-router"; |
import { useRouter } from "vue-router"; |
||||||
import { NoobHead, Api } from "noob-mengyxu" |
import { Api, NoobHead } from "noob-mengyxu"; |
||||||
const { Head, MenuTree, HeadPersonal, Fullscreen, StyleChange, LangChange, SizeChange } = NoobHead; |
|
||||||
import md5 from "js-md5"; |
import md5 from "js-md5"; |
||||||
|
|
||||||
|
const { Head, MenuTree, HeadPersonal, Fullscreen, StyleChange, LangChange, SizeChange } = NoobHead; |
||||||
|
|
||||||
const { state, commit, dispatch } = useStore(); |
const { state, commit, dispatch } = useStore(); |
||||||
const emit = defineEmits(['updatePwd', 'logout']); |
const emit = defineEmits(["updatePwd", "logout"]); |
||||||
const router = useRouter() |
const router = useRouter(); |
||||||
const appMain = reactive({ |
const appMain = reactive({ |
||||||
height: window.innerHeight + 'px', |
height: window.innerHeight + "px", |
||||||
backgroundColor: state.style.bodyBg |
backgroundColor: state.style.bodyBg, |
||||||
}); |
}); |
||||||
const main = ref(); |
const main = ref(); |
||||||
const flag = reactive({ |
const flag = reactive({ |
||||||
showHeader: true, |
showHeader: true, |
||||||
showAside: true, |
showAside: true, |
||||||
loading: false |
loading: false, |
||||||
}) |
}); |
||||||
let interval = 0; |
let interval = 0; |
||||||
|
|
||||||
const props = defineProps({ |
const props = defineProps({ |
||||||
title: { |
title: { |
||||||
type: String, |
type: String, |
||||||
default: null, |
default: null, |
||||||
}, |
}, |
||||||
menus: { |
menus: { |
||||||
type: Array<any>(), |
type: Array<any>(), |
||||||
default: null |
default: null, |
||||||
}, |
}, |
||||||
mode: { |
mode: { |
||||||
type: String, |
type: String, |
||||||
default: 'vertical', |
default: "vertical", |
||||||
}, |
}, |
||||||
styleAble: { |
styleAble: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
sizeAble: { |
sizeAble: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
langAble: { |
langAble: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
center: { |
center: { |
||||||
type: String, |
type: String, |
||||||
default: 'home', |
default: "home", |
||||||
}, |
}, |
||||||
checkUser: { |
checkUser: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
username: { |
username: { |
||||||
type: String, |
type: String, |
||||||
default: null, |
default: null, |
||||||
}, |
}, |
||||||
closeAble: { |
closeAble: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: false, |
default: false, |
||||||
}, |
}, |
||||||
logo: { |
logo: { |
||||||
type: String, |
type: String, |
||||||
default: null, |
default: null, |
||||||
}, |
}, |
||||||
}); |
}); |
||||||
|
|
||||||
const onResize = () => { |
const onResize = () => { |
||||||
const height = window.innerHeight; |
const height = window.innerHeight; |
||||||
appMain.height = height + 'px'; |
appMain.height = height + "px"; |
||||||
commit('initSize', [height, window.innerWidth]); |
commit("initSize", [height, window.innerWidth]); |
||||||
} |
}; |
||||||
|
|
||||||
const getUser = (first?) => { |
const getUser = (first?) => { |
||||||
if (!props.checkUser) { |
if (!props.checkUser) { |
||||||
return; |
return; |
||||||
|
} |
||||||
|
first && (flag.loading = true); |
||||||
|
Api.pub.getInfo().then((rsp) => { |
||||||
|
first && (flag.loading = false); |
||||||
|
if (rsp) { |
||||||
|
if (state.user?.userId !== rsp.userId) { |
||||||
|
commit("updateState", ["user", rsp]); |
||||||
|
} |
||||||
|
} else { |
||||||
|
router.push("/login"); |
||||||
} |
} |
||||||
first && (flag.loading = true); |
}); |
||||||
Api.pub.getInfo().then((rsp) => { |
|
||||||
first && (flag.loading = false); |
|
||||||
if (rsp) { |
|
||||||
if (state.user?.userId !== rsp.userId) { |
|
||||||
commit('updateState', ['user', rsp]); |
|
||||||
} |
|
||||||
} else { |
|
||||||
router.push('/login'); |
|
||||||
} |
|
||||||
}); |
|
||||||
}; |
}; |
||||||
|
|
||||||
const onLogout = () => { |
const onLogout = () => { |
||||||
Api.pub.logout().then(rsp => { |
Api.pub.logout().then((rsp) => { |
||||||
getUser(); |
getUser(); |
||||||
}); |
}); |
||||||
} |
}; |
||||||
|
|
||||||
const updatePwd = pwd => { |
const updatePwd = (pwd) => { |
||||||
pwd.old = md5(pwd.old); |
pwd.old = md5(pwd.old); |
||||||
pwd.new = md5(pwd.new); |
pwd.new = md5(pwd.new); |
||||||
pwd.reNew = md5(pwd.reNew); |
pwd.reNew = md5(pwd.reNew); |
||||||
emit('updatePwd', pwd); |
emit("updatePwd", pwd); |
||||||
Api.user.updatePwd(pwd); |
Api.user.updatePwd(pwd); |
||||||
} |
}; |
||||||
|
|
||||||
onMounted(() => { |
onMounted(() => { |
||||||
router.push("/") |
router.push("/"); |
||||||
dispatch("getMenus"); |
dispatch("getMenus"); |
||||||
getUser(true); |
getUser(true); |
||||||
interval = setInterval(getUser, 5000); |
interval = setInterval(getUser, 5000); |
||||||
window.onresize = onResize; |
window.onresize = onResize; |
||||||
onResize(); |
onResize(); |
||||||
}); |
}); |
||||||
|
|
||||||
onBeforeUnmount(() => { |
onBeforeUnmount(() => { |
||||||
clearInterval(interval); |
clearInterval(interval); |
||||||
}) |
}); |
||||||
|
|
||||||
</script> |
</script> |
||||||
|
|
||||||
<style lang='scss'> |
<style lang="scss"> |
||||||
@charset "UTF-8"; |
|
||||||
|
|
||||||
body { |
body { |
||||||
font-size: v-bind('state.size.fontSize') !important; |
font-size: v-bind("state.size.fontSize") !important; |
||||||
font-family: "Microsoft YaHei"; |
font-family: "Microsoft YaHei"; |
||||||
width: 100%; |
width: 100%; |
||||||
height: 100%; |
height: 100%; |
||||||
overflow: hidden; |
overflow: hidden; |
||||||
padding: 0px; |
padding: 0px; |
||||||
margin: 0px; |
margin: 0px; |
||||||
} |
} |
||||||
|
|
||||||
.app-main { |
.app-main { |
||||||
box-shadow: 2px 2px 5px 3px #e5e6eb; |
box-shadow: 2px 2px 5px 3px #e5e6eb; |
||||||
border-radius: 4px; |
border-radius: 4px; |
||||||
margin: 0px 0px 0px 3px !important; |
margin: 0px 0px 0px 3px !important; |
||||||
padding: 0 !important; |
padding: 0 !important; |
||||||
height: v-bind('state.size.mainHeight'); |
height: v-bind("state.size.mainHeight"); |
||||||
} |
} |
||||||
|
|
||||||
.el-header, |
.el-header, |
||||||
.main-head, |
.main-head, |
||||||
.main-table { |
.main-table { |
||||||
padding: 0; |
padding: 0; |
||||||
} |
} |
||||||
|
|
||||||
#app, |
#app, |
||||||
#container, |
#container, |
||||||
.app-main { |
.app-main { |
||||||
background-color: v-bind('state.style.bodyBg'); |
background-color: v-bind("state.style.bodyBg"); |
||||||
color: v-bind('state.style.color'); |
color: v-bind("state.style.color"); |
||||||
font-size: v-bind('state.size.fontSize'); |
font-size: v-bind("state.size.fontSize"); |
||||||
} |
} |
||||||
|
|
||||||
.app-head { |
.app-head { |
||||||
padding: 0px !important; |
padding: 0px !important; |
||||||
background-color: v-bind('state.style.headBg'); |
background-color: v-bind("state.style.headBg"); |
||||||
height: v-bind('state.size.headHeight'); |
height: v-bind("state.size.headHeight"); |
||||||
} |
} |
||||||
|
|
||||||
.head-icon { |
.head-icon { |
||||||
float: right; |
float: right; |
||||||
cursor: pointer; |
cursor: pointer; |
||||||
height: v-bind('state.size.headHeight'); |
height: v-bind("state.size.headHeight"); |
||||||
margin-right: 20px; |
margin-right: 20px; |
||||||
font-size: v-bind('state.size.headIconSize'); |
font-size: v-bind("state.size.headIconSize"); |
||||||
align-items: center; |
align-items: center; |
||||||
color: v-bind('state.style.color'); |
color: v-bind("state.style.color"); |
||||||
} |
} |
||||||
|
|
||||||
.form-item { |
.form-item { |
||||||
margin-right: v-bind('state.size.searchMargin'); |
margin-right: v-bind("state.size.searchMargin"); |
||||||
|
|
||||||
&.full { |
&.full { |
||||||
width: 100%; |
width: 100%; |
||||||
margin-right: 0px; |
margin-right: 0px; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
#app { |
#app { |
||||||
|
.el-input, |
||||||
.el-input, |
.el-textarea, |
||||||
.el-textarea, |
.el-date-editor, |
||||||
.el-date-editor, |
.el-input__wrapper { |
||||||
.el-input__wrapper { |
--el-input-bg-color: v-bind("state.style.itemBg") !important; |
||||||
--el-input-bg-color: v-bind('state.style.itemBg') !important; |
--el-fill-color-blank: v-bind("state.style.itemBg") !important; |
||||||
--el-fill-color-blank: v-bind('state.style.itemBg') !important; |
--el-input-text-color: v-bind("state.style.color") !important; |
||||||
--el-input-text-color: v-bind('state.style.color') !important; |
} |
||||||
} |
|
||||||
|
.el-popper { |
||||||
.el-popper { |
--el-bg-color-overlay: v-bind("state.style.itemBg") !important; |
||||||
--el-bg-color-overlay: v-bind('state.style.itemBg') !important; |
--el-fill-color-light: v-bind("state.style.bodyBg") !important; |
||||||
--el-fill-color-light: v-bind('state.style.bodyBg') !important; |
text-align: left; |
||||||
text-align: left; |
} |
||||||
} |
|
||||||
|
.el-dialog { |
||||||
.el-dialog { |
--el-dialog-bg-color: v-bind("state.style.bodyBg") !important; |
||||||
--el-dialog-bg-color: v-bind('state.style.bodyBg') !important; |
} |
||||||
} |
|
||||||
|
.el-drawer { |
||||||
.el-drawer { |
--el-drawer-bg-color: v-bind("state.style.bodyBg") !important; |
||||||
--el-drawer-bg-color: v-bind('state.style.bodyBg') !important; |
} |
||||||
} |
|
||||||
|
* { |
||||||
* { |
--el-text-color-regular: v-bind("state.style.color") !important; |
||||||
--el-text-color-regular: : v-bind('state.style.color') !important; |
--el-text-color-primary: v-bind("state.style.color") !important; |
||||||
--el-text-color-primary: v-bind('state.style.color') !important; |
--el-disabled-bg-color: v-bind("state.style.itemBg") !important; |
||||||
--el-disabled-bg-color: v-bind('state.style.itemBg') !important; |
} |
||||||
} |
|
||||||
|
*::selection { |
||||||
*::selection { |
background-color: v-bind("state.style.selectionBg"); |
||||||
background-color: v-bind('state.style.selectionBg'); |
color: v-bind("state.style.selectionColor"); |
||||||
color: v-bind('state.style.selectionColor'); |
} |
||||||
} |
|
||||||
} |
} |
||||||
|
|
||||||
</style> |
</style> |
||||||
@ -1,239 +1,234 @@ |
|||||||
<template> |
<template> |
||||||
<el-container :style="appMain" ref="main" v-show="!flag.loading"> |
<el-container :style="appMain" ref="main" v-show="!flag.loading"> |
||||||
<el-aside :width="state.size.asideWidth"> |
<el-aside :width="state.size.asideWidth"> |
||||||
<MenuTree :title="title || t('title')" :logo="logo" :data="menus" mode="vertical" /> |
<MenuTree :title="title || t('title')" :logo="logo" :data="menus" mode="vertical" /> |
||||||
</el-aside> |
</el-aside> |
||||||
<el-container id="container"> |
<el-container id="container"> |
||||||
<el-header v-show="state.size.headHeight != '0px'" class="app-head" :height="state.size.headHeight"> |
<el-header v-show="state.size.headHeight != '0px'" class="app-head" :height="state.size.headHeight"> |
||||||
|
<Head :title="title" :logo="logo" :username="username" :closeAble="true"> |
||||||
<Head :title="title" :logo="logo" :username="username" :closeAble="true"> |
<template #left> |
||||||
<template #left> |
<MenuTree v-show="state.size.asideWidth == '0px'" :data="menus" mode="horizontal" /> |
||||||
<MenuTree v-show="state.size.asideWidth == '0px'" :data="menus" mode="horizontal" /> |
</template> |
||||||
</template> |
<HeadPersonal @updatePwd="updatePwd" @logout="onLogout" :center="center" /> |
||||||
<HeadPersonal @updatePwd="updatePwd" @logout="onLogout" :center="center" /> |
<Fullscreen /> |
||||||
<Fullscreen /> |
<LangChange v-if="langAble" /> |
||||||
<LangChange v-if="langAble" /> |
<SizeChange v-if="sizeAble" /> |
||||||
<SizeChange v-if="sizeAble" /> |
<slot></slot> |
||||||
<slot></slot> |
</Head> |
||||||
</Head> |
</el-header> |
||||||
</el-header> |
<el-main class="app-main"> |
||||||
<el-main class="app-main"> |
<router-view /> |
||||||
<router-view /> |
</el-main> |
||||||
</el-main> |
|
||||||
</el-container> |
|
||||||
</el-container> |
</el-container> |
||||||
|
</el-container> |
||||||
</template> |
</template> |
||||||
|
|
||||||
<script lang="ts" setup> |
<script lang="ts" setup> |
||||||
import { reactive, onMounted, ref, onBeforeUnmount } from "vue"; |
import { reactive, onMounted, ref, onBeforeUnmount } from "vue"; |
||||||
import { useStore } from "vuex"; |
import { useStore } from "vuex"; |
||||||
import { useRouter } from "vue-router"; |
import { useRouter } from "vue-router"; |
||||||
import { NoobHead, Api, Styles, Size } from "noob-mengyxu" |
import { NoobHead, Api, Styles, Size } from "noob-mengyxu"; |
||||||
const { Head, MenuTree, HeadPersonal, Fullscreen, StyleChange, LangChange, SizeChange } = NoobHead; |
const { Head, MenuTree, HeadPersonal, Fullscreen, StyleChange, LangChange, SizeChange } = NoobHead; |
||||||
import md5 from "js-md5"; |
import md5 from "js-md5"; |
||||||
import { useI18n } from "vue3-i18n"; |
import { useI18n } from "vue3-i18n"; |
||||||
const { t } = useI18n(); |
const { t } = useI18n(); |
||||||
|
|
||||||
const { state, commit, dispatch } = useStore(); |
const { state, commit, dispatch } = useStore(); |
||||||
const emit = defineEmits(['updatePwd', 'logout']); |
const emit = defineEmits(["updatePwd", "logout"]); |
||||||
const router = useRouter() |
const router = useRouter(); |
||||||
const appMain = reactive({ |
const appMain = reactive({ |
||||||
height: window.innerHeight + 'px', |
height: window.innerHeight + "px", |
||||||
backgroundColor: state.style.bodyBg |
backgroundColor: state.style.bodyBg, |
||||||
}); |
}); |
||||||
const main = ref(); |
const main = ref(); |
||||||
const flag = reactive({ |
const flag = reactive({ |
||||||
showHeader: true, |
showHeader: true, |
||||||
showAside: true, |
showAside: true, |
||||||
loading: false |
loading: false, |
||||||
}) |
}); |
||||||
let interval = 0; |
let interval = 0; |
||||||
state.style = Styles.light; |
state.style = Styles.light; |
||||||
|
|
||||||
const props = defineProps({ |
const props = defineProps({ |
||||||
title: { |
title: { |
||||||
type: String, |
type: String, |
||||||
default: null, |
default: null, |
||||||
}, |
}, |
||||||
menus: { |
menus: { |
||||||
type: Array<any>(), |
type: Array<any>(), |
||||||
default: null |
default: null, |
||||||
}, |
}, |
||||||
styleAble: { |
styleAble: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
sizeAble: { |
sizeAble: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
langAble: { |
langAble: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
center: { |
center: { |
||||||
type: String, |
type: String, |
||||||
default: 'home', |
default: "home", |
||||||
}, |
}, |
||||||
checkUser: { |
checkUser: { |
||||||
type: Boolean, |
type: Boolean, |
||||||
default: true, |
default: true, |
||||||
}, |
}, |
||||||
username: { |
username: { |
||||||
type: String, |
type: String, |
||||||
default: null, |
default: null, |
||||||
}, |
}, |
||||||
logo: { |
logo: { |
||||||
type: String, |
type: String, |
||||||
default: null, |
default: null, |
||||||
}, |
}, |
||||||
}); |
}); |
||||||
|
|
||||||
const onResize = () => { |
const onResize = () => { |
||||||
const height = window.innerHeight; |
const height = window.innerHeight; |
||||||
appMain.height = height + 'px'; |
appMain.height = height + "px"; |
||||||
commit('initSize', [height, window.innerWidth]); |
commit("initSize", [height, window.innerWidth]); |
||||||
} |
}; |
||||||
|
|
||||||
const getUser = (first?) => { |
const getUser = (first?) => { |
||||||
if (!props.checkUser) { |
if (!props.checkUser) { |
||||||
return; |
return; |
||||||
|
} |
||||||
|
first && (flag.loading = true); |
||||||
|
Api.pub.getInfo().then((rsp) => { |
||||||
|
first && (flag.loading = false); |
||||||
|
if (rsp) { |
||||||
|
commit("updateState", ["user", rsp]); |
||||||
|
} else { |
||||||
|
router.push("/login"); |
||||||
} |
} |
||||||
first && (flag.loading = true); |
}); |
||||||
Api.pub.getInfo().then((rsp) => { |
|
||||||
first && (flag.loading = false); |
|
||||||
if (rsp) { |
|
||||||
commit('updateState', ['user', rsp]); |
|
||||||
} else { |
|
||||||
router.push('/login'); |
|
||||||
} |
|
||||||
}); |
|
||||||
}; |
}; |
||||||
|
|
||||||
const onLogout = () => { |
const onLogout = () => { |
||||||
Api.pub.logout().then(rsp => { |
Api.pub.logout().then((rsp) => { |
||||||
getUser(); |
getUser(); |
||||||
}); |
}); |
||||||
} |
}; |
||||||
|
|
||||||
const updatePwd = pwd => { |
const updatePwd = (pwd) => { |
||||||
pwd.old = md5(pwd.old); |
pwd.old = md5(pwd.old); |
||||||
pwd.new = md5(pwd.new); |
pwd.new = md5(pwd.new); |
||||||
pwd.reNew = md5(pwd.reNew); |
pwd.reNew = md5(pwd.reNew); |
||||||
emit('updatePwd', pwd); |
emit("updatePwd", pwd); |
||||||
Api.user.updatePwd(pwd); |
Api.user.updatePwd(pwd); |
||||||
} |
}; |
||||||
|
|
||||||
onMounted(() => { |
onMounted(() => { |
||||||
router.push("/") |
router.push("/"); |
||||||
dispatch("getMenus"); |
dispatch("getMenus"); |
||||||
getUser(true); |
getUser(true); |
||||||
interval = setInterval(getUser, 5000); |
interval = setInterval(getUser, 5000); |
||||||
window.onresize = onResize; |
window.onresize = onResize; |
||||||
onResize(); |
onResize(); |
||||||
commit('updateState', ['style', Styles.plain]); |
commit("updateState", ["style", Styles.plain]); |
||||||
}); |
}); |
||||||
|
|
||||||
onBeforeUnmount(() => { |
onBeforeUnmount(() => { |
||||||
clearInterval(interval); |
clearInterval(interval); |
||||||
}) |
}); |
||||||
|
|
||||||
</script> |
</script> |
||||||
|
|
||||||
<style lang='scss'> |
<style lang="scss"> |
||||||
@charset "UTF-8"; |
|
||||||
|
|
||||||
body { |
body { |
||||||
font-size: v-bind('state.size.fontSize') !important; |
font-size: v-bind("state.size.fontSize") !important; |
||||||
font-family: "Microsoft YaHei"; |
font-family: "Microsoft YaHei"; |
||||||
width: 100%; |
width: 100%; |
||||||
height: 100%; |
height: 100%; |
||||||
overflow: hidden; |
overflow: hidden; |
||||||
padding: 0px; |
padding: 0px; |
||||||
margin: 0px; |
margin: 0px; |
||||||
} |
} |
||||||
|
|
||||||
.app-main { |
.app-main { |
||||||
box-shadow: 2px 2px 5px 3px #e5e6eb; |
box-shadow: 2px 2px 5px 3px #e5e6eb; |
||||||
border-radius: 4px; |
border-radius: 4px; |
||||||
margin: 0px 0px 0px 3px !important; |
margin: 0px 0px 0px 3px !important; |
||||||
padding: 0 !important; |
padding: 0 !important; |
||||||
height: v-bind('state.size.mainHeight'); |
height: v-bind("state.size.mainHeight"); |
||||||
} |
} |
||||||
|
|
||||||
.el-header, |
.el-header, |
||||||
.main-head, |
.main-head, |
||||||
.main-table { |
.main-table { |
||||||
padding: 0; |
padding: 0; |
||||||
} |
} |
||||||
|
|
||||||
#app, |
#app, |
||||||
#container, |
#container, |
||||||
.app-main { |
.app-main { |
||||||
background-color: v-bind('state.style.bodyBg'); |
background-color: v-bind("state.style.bodyBg"); |
||||||
color: v-bind('state.style.color'); |
color: v-bind("state.style.color"); |
||||||
font-size: v-bind('state.size.fontSize'); |
font-size: v-bind("state.size.fontSize"); |
||||||
} |
} |
||||||
|
|
||||||
.app-head { |
.app-head { |
||||||
padding: 0px !important; |
padding: 0px !important; |
||||||
background-color: v-bind('state.style.headBg'); |
background-color: v-bind("state.style.headBg"); |
||||||
height: v-bind('state.size.headHeight'); |
height: v-bind("state.size.headHeight"); |
||||||
} |
} |
||||||
|
|
||||||
.head-icon { |
.head-icon { |
||||||
float: right; |
float: right; |
||||||
cursor: pointer; |
cursor: pointer; |
||||||
height: v-bind('state.size.headHeight') !important; |
height: v-bind("state.size.headHeight") !important; |
||||||
margin-right: 20px !important; |
margin-right: 20px !important; |
||||||
font-size: v-bind('state.size.headIconSize') !important; |
font-size: v-bind("state.size.headIconSize") !important; |
||||||
align-items: center !important; |
align-items: center !important; |
||||||
color: v-bind('state.style.color') !important; |
color: v-bind("state.style.color") !important; |
||||||
} |
} |
||||||
|
|
||||||
.form-item { |
.form-item { |
||||||
margin-right: v-bind('state.size.searchMargin'); |
margin-right: v-bind("state.size.searchMargin"); |
||||||
|
|
||||||
&.full { |
&.full { |
||||||
width: 100%; |
width: 100%; |
||||||
margin-right: 0px; |
margin-right: 0px; |
||||||
} |
} |
||||||
} |
} |
||||||
|
|
||||||
#app { |
#app { |
||||||
|
.el-input, |
||||||
.el-input, |
.el-textarea, |
||||||
.el-textarea, |
.el-date-editor, |
||||||
.el-date-editor, |
.el-input__wrapper { |
||||||
.el-input__wrapper { |
--el-input-bg-color: v-bind("state.style.itemBg") !important; |
||||||
--el-input-bg-color: v-bind('state.style.itemBg') !important; |
--el-fill-color-blank: v-bind("state.style.itemBg") !important; |
||||||
--el-fill-color-blank: v-bind('state.style.itemBg') !important; |
--el-input-text-color: v-bind("state.style.color") !important; |
||||||
--el-input-text-color: v-bind('state.style.color') !important; |
} |
||||||
} |
|
||||||
|
.el-popper { |
||||||
.el-popper { |
--el-bg-color-overlay: v-bind("state.style.itemBg") !important; |
||||||
--el-bg-color-overlay: v-bind('state.style.itemBg') !important; |
--el-fill-color-light: v-bind("state.style.bodyBg") !important; |
||||||
--el-fill-color-light: v-bind('state.style.bodyBg') !important; |
text-align: left; |
||||||
text-align: left; |
} |
||||||
} |
|
||||||
|
.el-dialog { |
||||||
.el-dialog { |
--el-dialog-bg-color: v-bind("state.style.bodyBg") !important; |
||||||
--el-dialog-bg-color: v-bind('state.style.bodyBg') !important; |
} |
||||||
} |
|
||||||
|
.el-drawer { |
||||||
.el-drawer { |
--el-drawer-bg-color: v-bind("state.style.bodyBg") !important; |
||||||
--el-drawer-bg-color: v-bind('state.style.bodyBg') !important; |
} |
||||||
} |
|
||||||
|
* { |
||||||
* { |
--el-text-color-regular: v-bind("state.style.color") !important; |
||||||
--el-text-color-regular: : v-bind('state.style.color') !important; |
--el-text-color-primary: v-bind("state.style.color") !important; |
||||||
--el-text-color-primary: v-bind('state.style.color') !important; |
--el-disabled-bg-color: v-bind("state.style.itemBg") !important; |
||||||
--el-disabled-bg-color: v-bind('state.style.itemBg') !important; |
} |
||||||
} |
|
||||||
|
*::selection { |
||||||
*::selection { |
background-color: v-bind("state.style.selectionBg"); |
||||||
background-color: v-bind('state.style.selectionBg'); |
color: v-bind("state.style.selectionColor"); |
||||||
color: v-bind('state.style.selectionColor'); |
} |
||||||
} |
|
||||||
} |
} |
||||||
</style> |
</style> |
||||||
@ -0,0 +1,11 @@ |
|||||||
|
<script setup lang="ts"> |
||||||
|
|
||||||
|
</script> |
||||||
|
|
||||||
|
<template> |
||||||
|
|
||||||
|
</template> |
||||||
|
|
||||||
|
<style scoped lang="scss"> |
||||||
|
|
||||||
|
</style> |
||||||
@ -0,0 +1,6 @@ |
|||||||
|
export * from "./useActionPers"; |
||||||
|
export * from "./useListTable"; |
||||||
|
export * from "./useLoading"; |
||||||
|
export * from "./useModifyForm"; |
||||||
|
export * from "./useSysDict"; |
||||||
|
export * from "./useWatchOnce"; |
||||||
@ -0,0 +1,32 @@ |
|||||||
|
import { onMounted, toRef } from "vue"; |
||||||
|
import { useRoute } from "vue-router"; |
||||||
|
import { useStore } from "vuex"; |
||||||
|
|
||||||
|
export function useActionPers(parent?: string) { |
||||||
|
const store = useStore(); |
||||||
|
|
||||||
|
if (!parent) { |
||||||
|
const route = useRoute(); |
||||||
|
parent = route.path; |
||||||
|
} |
||||||
|
|
||||||
|
const update = async () => { |
||||||
|
await store.dispatch("updateActionPers", parent); |
||||||
|
}; |
||||||
|
|
||||||
|
const actionPers = toRef(() => { |
||||||
|
// @ts-ignore
|
||||||
|
const pers = store.state.actionPers[parent] ?? []; |
||||||
|
return Object.fromEntries(pers.map((code) => [code, true])); |
||||||
|
}); |
||||||
|
|
||||||
|
onMounted(async () => { |
||||||
|
await update(); |
||||||
|
}); |
||||||
|
|
||||||
|
return { |
||||||
|
get: (per) => !!actionPers.value[per], |
||||||
|
update, |
||||||
|
actionPers, |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,86 @@ |
|||||||
|
import { toReactive } from "@vueuse/core"; |
||||||
|
import { reactive, ref, shallowRef, watchEffect } from "vue"; |
||||||
|
import * as Element from "../element"; |
||||||
|
import { useI18n } from "vue3-i18n"; |
||||||
|
import { PageResponse } from "../http"; |
||||||
|
|
||||||
|
const { showMessage } = Element; |
||||||
|
|
||||||
|
export interface TableColumn { |
||||||
|
code: string; |
||||||
|
name?: string; |
||||||
|
i18n?: string; |
||||||
|
width: number; |
||||||
|
dict?: string; |
||||||
|
slot?: boolean; |
||||||
|
|
||||||
|
[others: string]: any; |
||||||
|
} |
||||||
|
|
||||||
|
export interface TableData { |
||||||
|
rows: PageResponse<any>; |
||||||
|
example: Record<string, any>; |
||||||
|
} |
||||||
|
|
||||||
|
interface Options { |
||||||
|
query: (example: any) => Promise<PageResponse<any> | Record<string, any>[] | undefined>; |
||||||
|
initialPage?: number; |
||||||
|
initialPageSize?: number; |
||||||
|
initExample?: Record<string, any>; |
||||||
|
disableAutoQuery?: boolean; |
||||||
|
deep?: boolean; |
||||||
|
} |
||||||
|
|
||||||
|
export function useListTable(options: Options) { |
||||||
|
const { t } = useI18n(); |
||||||
|
const { initialPage, initialPageSize, initExample, deep } = options; |
||||||
|
|
||||||
|
type Row = Record<string, any>; |
||||||
|
type Rows = Row[] | PageResponse<Row>; |
||||||
|
|
||||||
|
const empty = <T>() => [] as T[]; |
||||||
|
const rows = deep ? ref<Rows>(empty()) : shallowRef<Rows>(empty()); |
||||||
|
|
||||||
|
const setRows = (resp?: Rows) => { |
||||||
|
if (resp == null) { |
||||||
|
rows.value = empty(); |
||||||
|
} else { |
||||||
|
rows.value = resp; |
||||||
|
if (Array.isArray(resp) || resp.total == null) { |
||||||
|
for (const key in pageParams) { |
||||||
|
delete example[key]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const pageParams = { |
||||||
|
page: initialPage || 1, |
||||||
|
size: initialPageSize || 10, |
||||||
|
}; |
||||||
|
|
||||||
|
const example = reactive<any>({ |
||||||
|
...pageParams, |
||||||
|
...initExample, |
||||||
|
}); |
||||||
|
|
||||||
|
const query = async () => { |
||||||
|
try { |
||||||
|
const resp = await options.query(example); |
||||||
|
setRows(resp); |
||||||
|
} catch (error) { |
||||||
|
showMessage("error", t("common.errors.listTableQueryError")); |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
if (!options.disableAutoQuery) { |
||||||
|
watchEffect(query); |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
rows: toReactive(rows), |
||||||
|
example, |
||||||
|
setRows, |
||||||
|
query, |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,18 @@ |
|||||||
|
// composables/useLoading.js
|
||||||
|
import { ElLoading } from 'element-plus' |
||||||
|
|
||||||
|
export function useLoading() { |
||||||
|
const showLoading = (options = {}) => { |
||||||
|
return ElLoading.service({ |
||||||
|
lock: true, |
||||||
|
text: 'Loading', |
||||||
|
spinner: 'el-icon-loading', |
||||||
|
background: 'rgba(0, 0, 0, 0.3)', |
||||||
|
...options |
||||||
|
}) |
||||||
|
} |
||||||
|
|
||||||
|
return { |
||||||
|
showLoading |
||||||
|
} |
||||||
|
} |
||||||
@ -0,0 +1,132 @@ |
|||||||
|
import { reactive, Ref } from "vue"; |
||||||
|
import {} from "noob-mengyxu"; |
||||||
|
import * as Element from "../element"; |
||||||
|
import { clearAndAssign, deepCopy } from "../util/objectUtil"; |
||||||
|
|
||||||
|
const { showMessage } = Element; |
||||||
|
|
||||||
|
export interface FormProp { |
||||||
|
code: string; |
||||||
|
name?: string; |
||||||
|
i18n?: string; |
||||||
|
dict?: string; |
||||||
|
slot?: boolean; |
||||||
|
|
||||||
|
[others: string]: any; |
||||||
|
} |
||||||
|
|
||||||
|
interface Options { |
||||||
|
props?: FormProp[]; |
||||||
|
formRef?: Ref<any>; |
||||||
|
handleAdd?: (value) => Promise<void>; |
||||||
|
handleEdit?: (value) => Promise<void>; |
||||||
|
handleCancel?: () => Promise<void>; |
||||||
|
handleError?: (kind: "validation" | "internal", err?) => void; |
||||||
|
init?: any; |
||||||
|
extraProps?: string[] | "any" | ((code: string) => boolean); |
||||||
|
} |
||||||
|
|
||||||
|
export function useModifyForm(options: Options) { |
||||||
|
const { formRef, handleAdd, handleEdit, handleCancel, init, extraProps } = options; |
||||||
|
const initValue = init ?? {}; |
||||||
|
const props = options.props ?? []; |
||||||
|
const propMap = Object.fromEntries(props.map((prop) => [prop.code, prop])); |
||||||
|
|
||||||
|
let isValidProp: (code: string) => boolean; |
||||||
|
|
||||||
|
if (typeof extraProps === "function") { |
||||||
|
isValidProp = (code: string) => extraProps(code) as boolean; |
||||||
|
} else if (Array.isArray(extraProps)) { |
||||||
|
isValidProp = (code: string) => extraProps.includes(code); |
||||||
|
} else { |
||||||
|
isValidProp = (_code: string) => extraProps === "any"; |
||||||
|
} |
||||||
|
|
||||||
|
const normalizeValue = (code: string, value: any) => { |
||||||
|
const prop = propMap[code]; |
||||||
|
if (!prop && !isValidProp(code)) return null; |
||||||
|
if (prop?.dict) { |
||||||
|
const parsed = parseInt(value); |
||||||
|
return isNaN(parsed) ? value : parsed; |
||||||
|
} |
||||||
|
|
||||||
|
return value; |
||||||
|
}; |
||||||
|
|
||||||
|
const normalizeModel = (newModel: Record<string, any>) => |
||||||
|
Object.fromEntries( |
||||||
|
Object.entries(newModel).flatMap(([code, value]) => { |
||||||
|
const normalized = normalizeValue(code, value); |
||||||
|
return normalized != null ? [[code, normalized]] : []; |
||||||
|
}) |
||||||
|
); |
||||||
|
|
||||||
|
const handleError = |
||||||
|
options.handleError ?? |
||||||
|
((kind, err) => { |
||||||
|
if (kind === "validation") { |
||||||
|
showMessage("error", "Validation Error"); |
||||||
|
} else if (kind === "internal") { |
||||||
|
const msg = `Internal Error: ${err}`; |
||||||
|
showMessage("error", msg); |
||||||
|
console.error(msg); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
return { |
||||||
|
flagOnClose: null, |
||||||
|
flagOnAdd: "add", |
||||||
|
flagOnEdit: "edit", |
||||||
|
|
||||||
|
data: reactive<{ dialog: any; model: Record<string, any> }>({ |
||||||
|
dialog: null, |
||||||
|
model: { ...initValue }, |
||||||
|
}), |
||||||
|
|
||||||
|
clearModel() { |
||||||
|
clearAndAssign(this.data.model, initValue); |
||||||
|
}, |
||||||
|
|
||||||
|
assignModel(newModel: Record<string, any>) { |
||||||
|
const normalized = normalizeModel(newModel); |
||||||
|
this.clearModel(); |
||||||
|
Object.assign(this.data.model, normalized); |
||||||
|
}, |
||||||
|
|
||||||
|
openAdd() { |
||||||
|
this.clearModel(); |
||||||
|
formRef?.value?.clearValidate(); |
||||||
|
this.data.dialog = this.flagOnAdd; |
||||||
|
}, |
||||||
|
|
||||||
|
openEdit(toEdit: Record<string, any>) { |
||||||
|
this.assignModel(toEdit); |
||||||
|
formRef?.value?.clearValidate(); |
||||||
|
this.data.dialog = this.flagOnEdit; |
||||||
|
}, |
||||||
|
|
||||||
|
close() { |
||||||
|
this.data.dialog = this.flagOnClose; |
||||||
|
}, |
||||||
|
|
||||||
|
async onConfirm() { |
||||||
|
const value = deepCopy(this.data.model); |
||||||
|
try { |
||||||
|
if (this.data.dialog === this.flagOnAdd) { |
||||||
|
await handleAdd?.(value); |
||||||
|
} else if (this.data.dialog === this.flagOnEdit) { |
||||||
|
await handleEdit?.(value); |
||||||
|
} |
||||||
|
} catch (err) { |
||||||
|
handleError?.("internal", err); |
||||||
|
} |
||||||
|
|
||||||
|
this.close(); |
||||||
|
}, |
||||||
|
|
||||||
|
async onCancel() { |
||||||
|
await handleCancel?.(); |
||||||
|
this.close(); |
||||||
|
}, |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,43 @@ |
|||||||
|
import { computed } from "vue"; |
||||||
|
import { useStore } from "vuex"; |
||||||
|
|
||||||
|
export function useSysDict() { |
||||||
|
type Callback = () => Record<string, string> | Promise<Record<string, string>>; |
||||||
|
const store = useStore(); |
||||||
|
const callbacks: Record<string, Callback> = {}; |
||||||
|
|
||||||
|
const normalize = (content: Record<string, string>) => { |
||||||
|
if (content == null) return {}; |
||||||
|
return Object.fromEntries( |
||||||
|
Object.entries(content).map(([k, v]) => { |
||||||
|
const parsed = parseInt(v); |
||||||
|
return [k, isNaN(parsed) ? v : parsed]; |
||||||
|
}) |
||||||
|
); |
||||||
|
}; |
||||||
|
|
||||||
|
const updateDict = async (dictIds: string[], registerCallbacks: Record<string, Callback> = {}) => { |
||||||
|
await store.dispatch("getDictMap", dictIds); |
||||||
|
Object.assign(callbacks, registerCallbacks); |
||||||
|
|
||||||
|
for (const id of dictIds) { |
||||||
|
const cb = callbacks[id]; |
||||||
|
const resp = await cb?.(); |
||||||
|
if (resp) { |
||||||
|
store.commit("updateDict", [id, normalize(resp)]); |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const unregister = (ids: string[]) => { |
||||||
|
for (const id of ids) { |
||||||
|
delete callbacks[id]; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
return { |
||||||
|
sysDict: computed<Record<string, Record<string, string>>>(() => store.state.dict), |
||||||
|
updateDict, |
||||||
|
unregister, |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,12 @@ |
|||||||
|
import { nextTick, Ref, watchEffect, WatchEffect } from "vue"; |
||||||
|
|
||||||
|
export function watchEffectOnce(cond: Ref<any, any>, cb: WatchEffect) { |
||||||
|
const stop = watchEffect((onCleanup) => { |
||||||
|
if (cond.value) { |
||||||
|
nextTick(() => stop()); |
||||||
|
cb(onCleanup); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
return stop; |
||||||
|
} |
||||||
@ -0,0 +1,68 @@ |
|||||||
|
import { type TableColumn, ListTableDialog, handleAsync } from "noob-mengyxu"; |
||||||
|
import { ElMessageBox } from "element-plus"; |
||||||
|
import { h } from "vue"; |
||||||
|
import { PageResponse } from "../http"; |
||||||
|
type Row = Record<string, any>; |
||||||
|
|
||||||
|
export interface ListTableProps { |
||||||
|
props: TableColumn[]; |
||||||
|
rowKey: string; |
||||||
|
initialPage?: number; |
||||||
|
initialPageSize?: number; |
||||||
|
initExample?: Record<string, any>; |
||||||
|
useDicts?: string[]; |
||||||
|
} |
||||||
|
|
||||||
|
interface Options { |
||||||
|
title: string; |
||||||
|
props: ListTableProps; |
||||||
|
query: (example: Record<string, any>) => Promise<PageResponse<Row>>; |
||||||
|
confirm: (rows: Row[]) => Promise<void>; |
||||||
|
children?: any; |
||||||
|
} |
||||||
|
|
||||||
|
export function showListTableDialog({ title, props, query, confirm, children }: Options) { |
||||||
|
const open = async () => { |
||||||
|
try { |
||||||
|
await ElMessageBox({ |
||||||
|
title, |
||||||
|
showConfirmButton: false, |
||||||
|
showCancelButton: false, |
||||||
|
message: h( |
||||||
|
ListTableDialog, |
||||||
|
{ |
||||||
|
...props, |
||||||
|
onQuery: handleAsync(query), |
||||||
|
onConfirm: handleAsync(async (rows) => { |
||||||
|
console.log("onConfirm"); |
||||||
|
await confirm(rows); |
||||||
|
}), |
||||||
|
onClose: handleAsync(async () => { |
||||||
|
console.log("onClose"); |
||||||
|
ElMessageBox.close(); |
||||||
|
}), |
||||||
|
}, |
||||||
|
children |
||||||
|
), |
||||||
|
customStyle: { |
||||||
|
maxWidth: "80%", |
||||||
|
}, |
||||||
|
}); |
||||||
|
} catch (err) { |
||||||
|
if (err === "cancel" || err === "close") { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
throw err; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const close = () => { |
||||||
|
ElMessageBox.close(); |
||||||
|
}; |
||||||
|
|
||||||
|
return { |
||||||
|
open, |
||||||
|
close, |
||||||
|
}; |
||||||
|
} |
||||||
@ -0,0 +1,318 @@ |
|||||||
|
/* eslint-disable */ |
||||||
|
import axios from "axios"; |
||||||
|
import * as Lang from "../i18n"; |
||||||
|
import { loading, close, showMessage } from "../element"; |
||||||
|
const { t } = Lang.i18n; |
||||||
|
|
||||||
|
let router; |
||||||
|
|
||||||
|
const config = { |
||||||
|
baseURL: process.env.VUE_APP_BASE_URL ? "/api" : "", |
||||||
|
timeout: 60 * 1000, |
||||||
|
withCredentials: true, // Check cross-site Access-Control
|
||||||
|
}; |
||||||
|
|
||||||
|
const _axios = axios.create(config); |
||||||
|
_axios.defaults.headers.post["Content-Type"] = "application/json;charset=UTF-8"; |
||||||
|
_axios.defaults.headers.put["Content-Type"] = "application/json;charset=UTF-8"; |
||||||
|
_axios.defaults.headers.delete["Content-Type"] = "application/json;charset=UTF-8"; |
||||||
|
|
||||||
|
export const registerBaseUrl = (url) => { |
||||||
|
_axios.defaults.baseURL = url; |
||||||
|
}; |
||||||
|
|
||||||
|
export const registerRouter = (routerP) => { |
||||||
|
router = routerP; |
||||||
|
}; |
||||||
|
|
||||||
|
// Add a request interceptor
|
||||||
|
_axios.interceptors.request.use( |
||||||
|
function (config) { |
||||||
|
const time = new Date().getTime().toString(); |
||||||
|
const params = config.params; |
||||||
|
if (params) { |
||||||
|
delEmpty(params); |
||||||
|
params.t = time; |
||||||
|
} |
||||||
|
const data = config.data; |
||||||
|
if (data != null && typeof data === "object") { |
||||||
|
delEmpty(data); |
||||||
|
data.t = time; |
||||||
|
} |
||||||
|
return config; |
||||||
|
}, |
||||||
|
function (error) { |
||||||
|
return Promise.reject(error); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
function delEmpty(data) { |
||||||
|
if (data) { |
||||||
|
for (const item in data) { |
||||||
|
if (data.hasOwnProperty(item)) { |
||||||
|
const val = data[item]; |
||||||
|
if ((val == null || val == "null" || val == "") && val !== 0 && val !== false) { |
||||||
|
delete data[item]; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Add a response interceptor
|
||||||
|
_axios.interceptors.response.use( |
||||||
|
function (response) { |
||||||
|
return response.data; |
||||||
|
}, |
||||||
|
function (error) { |
||||||
|
return Promise.reject(error); |
||||||
|
} |
||||||
|
); |
||||||
|
|
||||||
|
type QueryParams = URLSearchParams | Record<string, any> | ConstructorParameters<typeof URLSearchParams>[0]; |
||||||
|
|
||||||
|
export interface AxiosOptions { |
||||||
|
noMsg?: boolean | null; |
||||||
|
noLoading?: boolean | null; |
||||||
|
filter?: (response) => any; |
||||||
|
query?: QueryParams; |
||||||
|
} |
||||||
|
|
||||||
|
const defaultFilterResp = (resp) => { |
||||||
|
const isSuccess = resp.success || resp.status === "ok" || resp.status === "success" || resp.data != null; |
||||||
|
const isError = resp.success === false || resp.status === "error"; |
||||||
|
if (isSuccess) { |
||||||
|
return resp.data ? resp.data : resp; |
||||||
|
} else if (isError) { |
||||||
|
return false; |
||||||
|
} else return resp; |
||||||
|
}; |
||||||
|
|
||||||
|
export function post(url, data?, options: AxiosOptions = {}) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
if (!options.noLoading) { |
||||||
|
loading(); |
||||||
|
} |
||||||
|
_axios.post(url, data).then( |
||||||
|
(response: any) => { |
||||||
|
handResponse(response, resolve, options); |
||||||
|
}, |
||||||
|
(err) => { |
||||||
|
handError(err, reject, options); |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function mergeQueryParams(...params: Array<QueryParams>) { |
||||||
|
const res: URLSearchParams = new URLSearchParams(); |
||||||
|
for (const param of params) { |
||||||
|
if (param == null) continue; |
||||||
|
let parsed; |
||||||
|
if (param instanceof URLSearchParams) { |
||||||
|
parsed = param; |
||||||
|
} else { |
||||||
|
parsed = new URLSearchParams(param); |
||||||
|
} |
||||||
|
|
||||||
|
parsed |
||||||
|
.entries() |
||||||
|
.filter(([k, v]) => v != null && v != "null" && v != "undefined" && v != "") |
||||||
|
.forEach(([k, v]) => res?.append(k, v)); |
||||||
|
} |
||||||
|
|
||||||
|
return res ?? new URLSearchParams(); |
||||||
|
} |
||||||
|
|
||||||
|
export function get(url, data?: QueryParams, options: AxiosOptions = {}) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
if (!options.noLoading) { |
||||||
|
loading(); |
||||||
|
} |
||||||
|
_axios.get(url, { params: mergeQueryParams(data, options.query) }).then( |
||||||
|
(response: any) => { |
||||||
|
handResponse(response, resolve, options); |
||||||
|
}, |
||||||
|
(err) => { |
||||||
|
handError(err, reject, options); |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export function put(url, data?, options: AxiosOptions = {}) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
if (!options.noLoading) { |
||||||
|
loading(); |
||||||
|
} |
||||||
|
_axios.put(url, data, { params: mergeQueryParams(options.query) }).then( |
||||||
|
(response: any) => { |
||||||
|
handResponse(response, resolve, options); |
||||||
|
}, |
||||||
|
(err) => { |
||||||
|
handError(err, reject, options); |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export function delate(url, data?, options: AxiosOptions = {}) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
if (!options.noLoading) { |
||||||
|
loading(); |
||||||
|
} |
||||||
|
_axios({ |
||||||
|
method: "delete", |
||||||
|
url: url, |
||||||
|
data: data, |
||||||
|
params: mergeQueryParams(options.query), |
||||||
|
}).then( |
||||||
|
(response: any) => { |
||||||
|
handResponse(response, resolve, options); |
||||||
|
}, |
||||||
|
(err) => { |
||||||
|
handError(err, reject, options); |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
function handResponse(response, resolve, options: AxiosOptions = {}) { |
||||||
|
if (!options.noLoading) { |
||||||
|
close(); |
||||||
|
} |
||||||
|
|
||||||
|
const filterResponse = options.filter ?? defaultFilterResp; |
||||||
|
const result = filterResponse(response); |
||||||
|
|
||||||
|
if (result) { |
||||||
|
if (!options.noMsg && response.message) { |
||||||
|
showMessage("success", response.message); |
||||||
|
} |
||||||
|
|
||||||
|
resolve(result); |
||||||
|
} else { |
||||||
|
if (response.message == "session timeout") { |
||||||
|
router?.push("/login"); |
||||||
|
response.message = t("http.unLogin"); |
||||||
|
} |
||||||
|
if (response.message == "no permission") { |
||||||
|
response.message = t("http.unPermission"); |
||||||
|
} |
||||||
|
if (response.message?.indexOf("no permission for ") == 0) { |
||||||
|
response.message = t("http.noPermission"); |
||||||
|
} |
||||||
|
if (!options.noMsg && response.message) { |
||||||
|
showMessage("error", response.message); |
||||||
|
} |
||||||
|
response.error && console.log(response.error); |
||||||
|
resolve(false); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function handError(err, reject, options: AxiosOptions = {}) { |
||||||
|
if (!options.noLoading) { |
||||||
|
close(); |
||||||
|
} |
||||||
|
if (!options.noMsg) { |
||||||
|
showMessage("error", t("http.error")); |
||||||
|
} |
||||||
|
reject(err); |
||||||
|
} |
||||||
|
|
||||||
|
export function upload(file, url, data) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
let param = new FormData(); // 创建form对象
|
||||||
|
param.append("file", file); // 通过append向form对象添加数据
|
||||||
|
if (data) { |
||||||
|
for (const item in data) { |
||||||
|
if (data.hasOwnProperty(item)) { |
||||||
|
param.append(item, data[item]); // 添加form表单中其他数据
|
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
let config = { |
||||||
|
headers: { "Content-Type": "multipart/form-data" }, |
||||||
|
}; |
||||||
|
_axios.post(url, param, config).then( |
||||||
|
(response) => { |
||||||
|
handResponse(response, resolve); |
||||||
|
}, |
||||||
|
(err) => { |
||||||
|
handError(err, reject); |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export function getFile(url, data?, options: AxiosOptions = {}) { |
||||||
|
return new Promise((resolve, reject) => { |
||||||
|
if (!options.noLoading) { |
||||||
|
loading(); |
||||||
|
} |
||||||
|
_axios({ |
||||||
|
method: "get", |
||||||
|
url: url, // 请求地址
|
||||||
|
params: data, // 参数
|
||||||
|
responseType: "blob", // 表明返回服务器返回的数据类型
|
||||||
|
}).then( |
||||||
|
(response: any) => { |
||||||
|
handResponse(response, resolve, options); |
||||||
|
}, |
||||||
|
(err) => { |
||||||
|
handError(err, reject, options); |
||||||
|
} |
||||||
|
); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export function download(fileName, url, data = {}, callBack?) { |
||||||
|
loading(); |
||||||
|
_axios({ |
||||||
|
method: "get", |
||||||
|
url: url, // 请求地址
|
||||||
|
params: data, // 参数
|
||||||
|
responseType: "blob", // 表明返回服务器返回的数据类型
|
||||||
|
}).then( |
||||||
|
(response: any) => { |
||||||
|
close(); |
||||||
|
const reader = new FileReader() as any; |
||||||
|
reader.readAsText(response); |
||||||
|
reader.onload = function () { |
||||||
|
try { |
||||||
|
const result = JSON.parse(reader.result); |
||||||
|
if (typeof result === "object") { |
||||||
|
showMessage("error", result.message, false); |
||||||
|
if (callBack != null) { |
||||||
|
callBack(false); |
||||||
|
} |
||||||
|
return; |
||||||
|
} |
||||||
|
} catch (err) {} |
||||||
|
const blob = new Blob([response], { |
||||||
|
type: "application/octet-stream", |
||||||
|
}); |
||||||
|
const navigator = window.navigator as any; |
||||||
|
if (navigator.msSaveOrOpenBlob) { |
||||||
|
navigator.msSaveBlob(blob, fileName); |
||||||
|
} else { |
||||||
|
const link = document.createElement("a"); |
||||||
|
link.href = window.URL.createObjectURL(blob); |
||||||
|
link.download = fileName; |
||||||
|
link.click(); |
||||||
|
window.URL.revokeObjectURL(link.href); |
||||||
|
} |
||||||
|
if (callBack != null) { |
||||||
|
callBack(true); |
||||||
|
} |
||||||
|
}; |
||||||
|
}, |
||||||
|
(err) => { |
||||||
|
close(); |
||||||
|
showMessage("error", t("http.downFail")); |
||||||
|
if (callBack != null) { |
||||||
|
callBack(false); |
||||||
|
} |
||||||
|
} |
||||||
|
); |
||||||
|
} |
||||||
@ -1,2 +1,4 @@ |
|||||||
export * as Axios from './axios'; |
export * as Axios from "./axios"; |
||||||
export * as Axios2 from './axios2'; |
export * as Axios2 from "./axios2"; |
||||||
|
export * as Axios3 from "./axios3"; |
||||||
|
export * from "./misc"; |
||||||
|
|||||||
@ -0,0 +1,10 @@ |
|||||||
|
export interface PageResponse<T> { |
||||||
|
data: T[]; |
||||||
|
page?: number; |
||||||
|
total: number; |
||||||
|
} |
||||||
|
|
||||||
|
export const pageEmpty = <T>(): PageResponse<T> => ({ |
||||||
|
data: [], |
||||||
|
total: 0, |
||||||
|
}); |
||||||
@ -1,7 +1,10 @@ |
|||||||
export * from './config'; |
export * from "./config"; |
||||||
export * as Element from './element'; |
export * as Element from "./element"; |
||||||
export * as Store from './store'; |
export * as Store from "./store"; |
||||||
export * as Http from './http'; |
export * as Http from "./http"; |
||||||
export * as Lang from './i18n'; |
export * as Lang from "./i18n"; |
||||||
export * as Api from './api'; |
export * as Api from "./api"; |
||||||
export * from './constant'; |
export * from "./constant"; |
||||||
|
export * from "./util/asyncUtil"; |
||||||
|
export * from "./util/objectUtil"; |
||||||
|
export * from "./composables"; |
||||||
|
|||||||
@ -0,0 +1,21 @@ |
|||||||
|
export type AsyncHandler<T> = { |
||||||
|
resolve: (t: T | PromiseLike<T>) => void; |
||||||
|
reject: (err: any) => void; |
||||||
|
}; |
||||||
|
|
||||||
|
type ExtractResult<Args extends any[]> = Args extends [AsyncHandler<infer T>, ...any[]] ? T : never; |
||||||
|
|
||||||
|
type ExtractArgs<Args extends any[]> = Args extends [AsyncHandler<any>, ...infer Rest] ? Rest : never; |
||||||
|
|
||||||
|
export function useAsyncEmits<Emits extends Record<string, any[]>>(emits: (evt: any, ...args: any[]) => void) { |
||||||
|
const emitsAsync = <Evt extends keyof Emits>(evt: Evt, ...args: ExtractArgs<Emits[Evt]>) => |
||||||
|
new Promise<ExtractResult<Emits[Evt]>>((resolve, reject) => { |
||||||
|
emits(evt, { resolve, reject }, ...args); |
||||||
|
}); |
||||||
|
|
||||||
|
return emitsAsync; |
||||||
|
} |
||||||
|
|
||||||
|
export function handleAsync<T, Args extends any[]>(handler: (...args: Args) => Promise<T>) { |
||||||
|
return ({ resolve, reject }: AsyncHandler<T>, ...args: Args) => handler(...args).then(resolve, reject); |
||||||
|
} |
||||||
@ -0,0 +1,22 @@ |
|||||||
|
import { cloneDeep } from "lodash-es"; |
||||||
|
|
||||||
|
export function deepCopy<T = any>(obj: T): T { |
||||||
|
return cloneDeep(obj); |
||||||
|
} |
||||||
|
|
||||||
|
export function clearObject(obj: Record<string, any>) { |
||||||
|
for (const key in obj) { |
||||||
|
delete obj[key]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export function clearAndAssign(target: Record<string, any>, source: Record<string, any>) { |
||||||
|
clearObject(target); |
||||||
|
Object.assign(target, source); |
||||||
|
} |
||||||
|
|
||||||
|
export function unnest(obj: Record<string, any>, key: string, prefix: string) { |
||||||
|
const { [key]: toFlatten, ...rest } = obj; |
||||||
|
const prefixed = Object.fromEntries(Object.entries(toFlatten).map(([k, v]) => [prefix.concat(k), v])); |
||||||
|
return { ...rest, ...prefixed }; |
||||||
|
} |
||||||
Loading…
Reference in new issue