门户首页
Showing
45 changed files
with
2090 additions
and
193 deletions
| ... | @@ -101,6 +101,12 @@ VITE_APP_SERVICE_BASEURL = ms-daop-trust-api-service | ... | @@ -101,6 +101,12 @@ VITE_APP_SERVICE_BASEURL = ms-daop-trust-api-service |
| 101 | #数字合约接口,身份认证有关的服务 | 101 | #数字合约接口,身份认证有关的服务 |
| 102 | VITE_APP_DIGITAL_CONTRACT_URL = ms-daop-trust-data-space-service | 102 | VITE_APP_DIGITAL_CONTRACT_URL = ms-daop-trust-data-space-service |
| 103 | 103 | ||
| 104 | # 是否启用登录验证 | ||
| 105 | VITE_verify = true | ||
| 106 | |||
| 107 | # 应用标识 | ||
| 108 | VITE_appKey = '691689d8e4b0f359c04d204a' | ||
| 109 | |||
| 104 | # 本地访问地址 | 110 | # 本地访问地址 |
| 105 | # VITE_API_CIRCULATION_URL = http://localhost:9000/circulation | 111 | # VITE_API_CIRCULATION_URL = http://localhost:9000/circulation |
| 106 | 112 | ... | ... |
| ... | @@ -133,6 +133,9 @@ VITE_APP_CIRCULATION = http://192.168.6.20:18052/ | ... | @@ -133,6 +133,9 @@ VITE_APP_CIRCULATION = http://192.168.6.20:18052/ |
| 133 | #数据加工交付 | 133 | #数据加工交付 |
| 134 | VITE_APP_DATA_DELIVERY = http://192.168.6.20:38052/ | 134 | VITE_APP_DATA_DELIVERY = http://192.168.6.20:38052/ |
| 135 | 135 | ||
| 136 | # 是否启用登录验证 | ||
| 137 | VITE_verify = true | ||
| 138 | |||
| 136 | # 是否在打包时生成 sourcemap | 139 | # 是否在打包时生成 sourcemap |
| 137 | VITE_BUILD_SOURCEMAP = false | 140 | VITE_BUILD_SOURCEMAP = false |
| 138 | # 是否在打包时开启压缩,支持 gzip 和 brotli | 141 | # 是否在打包时开启压缩,支持 gzip 和 brotli | ... | ... |
| ... | @@ -21,6 +21,7 @@ declare module '@vue/runtime-core' { | ... | @@ -21,6 +21,7 @@ declare module '@vue/runtime-core' { |
| 21 | Dialog_form: typeof import('./src/components/Dialog/dialog_form.vue')['default'] | 21 | Dialog_form: typeof import('./src/components/Dialog/dialog_form.vue')['default'] |
| 22 | Dialog_grid: typeof import('./src/components/Dialog/dialog_grid.vue')['default'] | 22 | Dialog_grid: typeof import('./src/components/Dialog/dialog_grid.vue')['default'] |
| 23 | Dialog_pane: typeof import('./src/components/Dialog/dialog_pane.vue')['default'] | 23 | Dialog_pane: typeof import('./src/components/Dialog/dialog_pane.vue')['default'] |
| 24 | DialogPlus: typeof import('./src/components/DialogPlus/src/DialogPlus.vue')['default'] | ||
| 24 | Drawer: typeof import('./src/components/Drawer/index.vue')['default'] | 25 | Drawer: typeof import('./src/components/Drawer/index.vue')['default'] |
| 25 | EchartsMap: typeof import('./src/components/EchartsMap/index.vue')['default'] | 26 | EchartsMap: typeof import('./src/components/EchartsMap/index.vue')['default'] |
| 26 | Editor: typeof import('./src/components/Editor/src/Editor.vue')['default'] | 27 | Editor: typeof import('./src/components/Editor/src/Editor.vue')['default'] |
| ... | @@ -28,13 +29,17 @@ declare module '@vue/runtime-core' { | ... | @@ -28,13 +29,17 @@ declare module '@vue/runtime-core' { |
| 28 | FileUpload: typeof import('./src/components/FileUpload/index.vue')['default'] | 29 | FileUpload: typeof import('./src/components/FileUpload/index.vue')['default'] |
| 29 | FixedActionBar: typeof import('./src/components/FixedActionBar/index.vue')['default'] | 30 | FixedActionBar: typeof import('./src/components/FixedActionBar/index.vue')['default'] |
| 30 | Form: typeof import('./src/components/Form/index.vue')['default'] | 31 | Form: typeof import('./src/components/Form/index.vue')['default'] |
| 32 | FormItem: typeof import('./src/components/FormItem/FormItem.vue')['default'] | ||
| 33 | FormPlus: typeof import('./src/components/FormPlus/src/FormPlus.vue')['default'] | ||
| 31 | GraphTopbar: typeof import('./src/components/RelationNetwork/graphTopbar.vue')['default'] | 34 | GraphTopbar: typeof import('./src/components/RelationNetwork/graphTopbar.vue')['default'] |
| 35 | Header: typeof import('./src/components/Header/index.vue')['default'] | ||
| 32 | Hour: typeof import('./src/components/Schedule/component/hour.vue')['default'] | 36 | Hour: typeof import('./src/components/Schedule/component/hour.vue')['default'] |
| 33 | ImagePreview: typeof import('./src/components/ImagePreview/index.vue')['default'] | 37 | ImagePreview: typeof import('./src/components/ImagePreview/index.vue')['default'] |
| 34 | ImagesUpload: typeof import('./src/components/ImagesUpload/index.vue')['default'] | 38 | ImagesUpload: typeof import('./src/components/ImagesUpload/index.vue')['default'] |
| 35 | ImageUpload: typeof import('./src/components/ImageUpload/index.vue')['default'] | 39 | ImageUpload: typeof import('./src/components/ImageUpload/index.vue')['default'] |
| 36 | LineageGraph: typeof import('./src/components/LineageGraph/index.vue')['default'] | 40 | LineageGraph: typeof import('./src/components/LineageGraph/index.vue')['default'] |
| 37 | ListPanel: typeof import('./src/components/ListPanel/index.vue')['default'] | 41 | ListPanel: typeof import('./src/components/ListPanel/index.vue')['default'] |
| 42 | Logo: typeof import('./src/components/Logo/index.vue')['default'] | ||
| 38 | LookBpmn: typeof import('./src/components/ApprovalProcess/src/components/LookBpmn.vue')['default'] | 43 | LookBpmn: typeof import('./src/components/ApprovalProcess/src/components/LookBpmn.vue')['default'] |
| 39 | Month: typeof import('./src/components/Schedule/component/month.vue')['default'] | 44 | Month: typeof import('./src/components/Schedule/component/month.vue')['default'] |
| 40 | NotAllowed: typeof import('./src/components/NotAllowed/index.vue')['default'] | 45 | NotAllowed: typeof import('./src/components/NotAllowed/index.vue')['default'] |
| ... | @@ -46,6 +51,7 @@ declare module '@vue/runtime-core' { | ... | @@ -46,6 +51,7 @@ declare module '@vue/runtime-core' { |
| 46 | PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default'] | 51 | PcasCascader: typeof import('./src/components/PcasCascader/index.vue')['default'] |
| 47 | Popover: typeof import('./src/components/Popover/index.vue')['default'] | 52 | Popover: typeof import('./src/components/Popover/index.vue')['default'] |
| 48 | RelationNetwork: typeof import('./src/components/RelationNetwork/index.vue')['default'] | 53 | RelationNetwork: typeof import('./src/components/RelationNetwork/index.vue')['default'] |
| 54 | Retrievepassword: typeof import('./src/components/Retrievepassword/index.vue')['default'] | ||
| 49 | RouterLink: typeof import('vue-router')['RouterLink'] | 55 | RouterLink: typeof import('vue-router')['RouterLink'] |
| 50 | RouterView: typeof import('vue-router')['RouterView'] | 56 | RouterView: typeof import('vue-router')['RouterView'] |
| 51 | Schedule: typeof import('./src/components/Schedule/index.vue')['default'] | 57 | Schedule: typeof import('./src/components/Schedule/index.vue')['default'] | ... | ... |
public/config.json
0 → 100644
src/api/modules/idaas.ts
0 → 100644
| 1 | import request from "@/utils/request"; | ||
| 2 | |||
| 3 | export const idaasLogin = (params) => request({ | ||
| 4 | url: `/oauth/login`, | ||
| 5 | method: 'idaasPost', | ||
| 6 | headers: { | ||
| 7 | 'Content-Type': 'application/x-www-form-urlencoded' | ||
| 8 | }, | ||
| 9 | data: params, | ||
| 10 | }) | ||
| 11 | |||
| 12 | export const getLoginWebAuthn = () => request({ | ||
| 13 | url: `/webauthn/assertion/options`, | ||
| 14 | method: 'idaasGet', | ||
| 15 | }) | ||
| 16 | |||
| 17 | export const getPictureCode = () => request({ | ||
| 18 | url: `/user/get-picture-code`, | ||
| 19 | method: 'get', | ||
| 20 | }) | ||
| 21 | |||
| 22 | // 通用图形验证码 | ||
| 23 | export const commoncheckImgCode = () => request({ | ||
| 24 | url: `/ms-daop-user-service/user/validate-code/get`, | ||
| 25 | method: 'get', | ||
| 26 | responseType: 'blob' | ||
| 27 | }) | ||
| 28 | |||
| 29 | // 校验图形验证码 | ||
| 30 | export const checkImgCode = (params) => request({ | ||
| 31 | url: `${import.meta.env.VITE_APP_AUTH_URL}/portal/check`, | ||
| 32 | method: 'post', | ||
| 33 | data: params | ||
| 34 | }); | ||
| 35 | |||
| 36 | // 获取图形验证码 | ||
| 37 | export const getImgCodeSrc = (params) => request({ | ||
| 38 | url: `${import.meta.env.VITE_APP_AUTH_URL}/portal/get-captcha`, | ||
| 39 | method: 'get', | ||
| 40 | params | ||
| 41 | }); | ||
| 42 | |||
| 43 | export const checkDeviceTypeRegist = (params) => request({ | ||
| 44 | url: `/web-authn/check-device-type-regist?logonUser=${params.logonUser}&deviceType=${params.platform}`, | ||
| 45 | method: 'idaasGet' | ||
| 46 | }); | ||
| 47 | |||
| 48 | /** | ||
| 49 | * 校验登录用户账号和密码 | ||
| 50 | * @param logonUser 用户手机号 | ||
| 51 | * @returns | ||
| 52 | */ | ||
| 53 | export const checkLoginUser = (logonUser:string) => request({ | ||
| 54 | url: `/ms-daop-user-service/user/check-login-user-password?logonUser=${logonUser}`, | ||
| 55 | method: 'get' | ||
| 56 | }); | ||
| 57 | |||
| 58 | export const registWebAuthn = () => request({ | ||
| 59 | url: `/webauthn/attestation/options`, | ||
| 60 | method: 'idaasGet' | ||
| 61 | }); | ||
| 62 | |||
| 63 | export const signUp = (data) => request({ | ||
| 64 | url: `/web-authn/signup`, | ||
| 65 | method: 'idaasPost', | ||
| 66 | data: data | ||
| 67 | }); | ||
| 68 | |||
| 69 | /** 发送登录验证码到手机 */ | ||
| 70 | export const sendLoginCode = (mobileNo:string) => request({ | ||
| 71 | url: `/ms-daop-user-service/user/get-login-sms-validate-code?mobileNo=${mobileNo}`, | ||
| 72 | method: 'get' | ||
| 73 | }); | ||
| 74 | |||
| 75 | export const getWebAuth4jLogin = (data) => request({ | ||
| 76 | url: `/webauthn/login`, | ||
| 77 | method: 'idaasPost', | ||
| 78 | data: data | ||
| 79 | }); | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/assets/images/avatar.png
0 → 100644
1.32 KB
src/assets/images/数据资产运营平台logo.png
0 → 100644
24.8 KB
src/assets/images/知识库.png
0 → 100644
1.03 KB
src/components/DialogPlus/index.ts
0 → 100644
src/components/DialogPlus/src/DialogPlus.vue
0 → 100644
| 1 | <script setup lang="ts"> | ||
| 2 | import { ElDialog, ElScrollbar } from 'element-plus' | ||
| 3 | import { propTypes } from '@/utils/propTypes' | ||
| 4 | import { computed, useAttrs, ref, unref, useSlots, watch, nextTick } from 'vue' | ||
| 5 | const slots = useSlots() | ||
| 6 | |||
| 7 | const props = defineProps({ | ||
| 8 | modelValue: propTypes.bool.def(false), | ||
| 9 | scrollbar: propTypes.bool.def(true), | ||
| 10 | title: propTypes.string.def('Dialog'), | ||
| 11 | fullscreen: propTypes.bool.def(true), | ||
| 12 | maxHeight: propTypes.oneOfType([String, Number]).def('400px'), | ||
| 13 | minHeight: propTypes.oneOfType([String, Number]).def('140px') | ||
| 14 | }) | ||
| 15 | |||
| 16 | const getBindValue = computed(() => { | ||
| 17 | const delArr: string[] = ['fullscreen', 'title', 'maxHeight'] | ||
| 18 | const attrs = useAttrs() | ||
| 19 | const obj = { ...attrs, ...props } | ||
| 20 | for (const key in obj) { | ||
| 21 | if (delArr.indexOf(key) !== -1) { | ||
| 22 | delete obj[key] | ||
| 23 | } | ||
| 24 | } | ||
| 25 | return obj | ||
| 26 | }) | ||
| 27 | |||
| 28 | const isFullscreen = ref(false) | ||
| 29 | |||
| 30 | const isNumber = (val) => { | ||
| 31 | return val && typeof props.maxHeight == 'number' | ||
| 32 | |||
| 33 | } | ||
| 34 | |||
| 35 | const dialogHeight = ref(isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight) | ||
| 36 | |||
| 37 | watch( | ||
| 38 | () => isFullscreen.value, | ||
| 39 | async (val: boolean) => { | ||
| 40 | await nextTick() | ||
| 41 | if (val) { | ||
| 42 | const windowHeight = document.documentElement.offsetHeight | ||
| 43 | dialogHeight.value = `${windowHeight - 55 - 60 - (slots.footer ? 63 : 0)}px` | ||
| 44 | } else { | ||
| 45 | dialogHeight.value = isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight | ||
| 46 | } | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | immediate: true | ||
| 50 | } | ||
| 51 | ) | ||
| 52 | |||
| 53 | const dialogStyle = computed(() => { | ||
| 54 | return { | ||
| 55 | height: unref(dialogHeight), | ||
| 56 | 'max-height': isNumber(props.maxHeight) ? `${props.maxHeight}px` : props.maxHeight, | ||
| 57 | 'min-height': isNumber(props.minHeight) ? `${props.minHeight}px` : props.minHeight, | ||
| 58 | } | ||
| 59 | }) | ||
| 60 | </script> | ||
| 61 | |||
| 62 | <template> | ||
| 63 | <ElDialog v-bind="getBindValue" :fullscreen="isFullscreen" destroy-on-close lock-scroll draggable top="15vh" | ||
| 64 | :close-on-click-modal="false" :show-close="true"> | ||
| 65 | <template #header="{ close }"> | ||
| 66 | <div class="flex justify-between items-center h-54px relative"> | ||
| 67 | <slot name="title"> | ||
| 68 | {{ title }} | ||
| 69 | </slot> | ||
| 70 | <!-- <div class="h-54px flex justify-between items-center absolute top-[50%] right-15px translate-y-[-50%]"> | ||
| 71 | <Icon v-if="fullscreen" class="cursor-pointer is-hover !h-54px mr-10px" | ||
| 72 | :icon="isFullscreen ? 'radix-icons:exit-full-screen' : 'radix-icons:enter-full-screen'" | ||
| 73 | color="var(--el-color-info)" hover-color="var(--el-color-primary)" @click="toggleFull" /> | ||
| 74 | <Icon class="cursor-pointer is-hover !h-54px" icon="ep:close" hover-color="var(--el-color-primary)" | ||
| 75 | color="var(--el-color-info)" @click="close" /> | ||
| 76 | </div> --> | ||
| 77 | </div> | ||
| 78 | </template> | ||
| 79 | |||
| 80 | <ElScrollbar v-if="scrollbar" :style="dialogStyle"> | ||
| 81 | <slot></slot> | ||
| 82 | </ElScrollbar> | ||
| 83 | <div v-else :style="dialogStyle"> | ||
| 84 | <slot></slot> | ||
| 85 | </div> | ||
| 86 | |||
| 87 | <template v-if="slots.footer" #footer> | ||
| 88 | <slot name="footer"></slot> | ||
| 89 | </template> | ||
| 90 | </ElDialog> | ||
| 91 | </template> | ||
| 92 | |||
| 93 | <style lang="scss" scoped> | ||
| 94 | .el-overlay-dialog { | ||
| 95 | display: flex; | ||
| 96 | justify-content: center; | ||
| 97 | align-items: center; | ||
| 98 | |||
| 99 | :deep .el-dialog { | ||
| 100 | margin: 0 !important; | ||
| 101 | |||
| 102 | :deep .el-dialog__header { | ||
| 103 | padding: 0 20px; | ||
| 104 | height: 50px; | ||
| 105 | border-bottom: 1px solid #d9d9d9; | ||
| 106 | margin-right: 0; | ||
| 107 | display: block !important; | ||
| 108 | align-items: center; | ||
| 109 | |||
| 110 | .el-dialog__headerbtn { | ||
| 111 | width: 50px; | ||
| 112 | height: 50px; | ||
| 113 | top: 0; | ||
| 114 | } | ||
| 115 | } | ||
| 116 | |||
| 117 | &__header { | ||
| 118 | height: 54px; | ||
| 119 | padding: 0; | ||
| 120 | margin-right: 0 !important; | ||
| 121 | border-bottom: 1px solid var(--el-border-color); | ||
| 122 | } | ||
| 123 | |||
| 124 | &__body { | ||
| 125 | padding: 15px !important; | ||
| 126 | } | ||
| 127 | |||
| 128 | &__footer { | ||
| 129 | border-top: 1px solid var(--el-border-color); | ||
| 130 | } | ||
| 131 | |||
| 132 | &__headerbtn { | ||
| 133 | top: 0; | ||
| 134 | } | ||
| 135 | } | ||
| 136 | } | ||
| 137 | </style> |
src/components/FormItem/FormItem.vue
0 → 100644
| 1 | <script setup lang="ts"> | ||
| 2 | import { FormPlus, FormSchema } from '@/components/FormPlus' | ||
| 3 | import { useForm } from '@/hooks/useForm' | ||
| 4 | import { propTypes } from "@/utils/propTypes"; | ||
| 5 | import { reactive, unref, ref, computed } from 'vue' | ||
| 6 | import { ElButton, ElInput, FormItemProp } from 'element-plus' | ||
| 7 | import { useValidator } from '@/hooks/useValidator' | ||
| 8 | import { ComponentRef } from '@/types/global'; | ||
| 9 | |||
| 10 | const props = defineProps({ | ||
| 11 | schemaParam: { | ||
| 12 | type: Object as PropType<FormSchema[]>, | ||
| 13 | default: () => { | ||
| 14 | return [] | ||
| 15 | } | ||
| 16 | }, | ||
| 17 | labelPosition: propTypes.string.def('top'), | ||
| 18 | disabled: propTypes.bool.def(false), | ||
| 19 | model: { | ||
| 20 | type: Object as PropType<any>, | ||
| 21 | default: () => { | ||
| 22 | return {} | ||
| 23 | } | ||
| 24 | } | ||
| 25 | }) | ||
| 26 | |||
| 27 | const formModel = computed(() => { | ||
| 28 | console.log(unref(props.model), 'model'); | ||
| 29 | return props.model | ||
| 30 | }) | ||
| 31 | const schemaData = computed(() => reactive<FormSchema[]>(unref(props.schemaParam))) | ||
| 32 | |||
| 33 | const { scrollToError } = useValidator() | ||
| 34 | |||
| 35 | |||
| 36 | const { formRegister, formMethods } = useForm() | ||
| 37 | const { | ||
| 38 | getFormResult, | ||
| 39 | setProps, | ||
| 40 | setValues, | ||
| 41 | setSchema, | ||
| 42 | getComponentExpose, | ||
| 43 | getFormItemExpose, | ||
| 44 | getElFormExpose, | ||
| 45 | getFormData | ||
| 46 | } = formMethods | ||
| 47 | |||
| 48 | /** | ||
| 49 | * 表单根据字段校验 | ||
| 50 | * @param fields 待验证的字段 | ||
| 51 | */ | ||
| 52 | const formValidation = async (fields: string[] = []) => { // 表单验证 | ||
| 53 | const elFormExpose = await getElFormExpose() | ||
| 54 | return new Promise(async (resolve) => { | ||
| 55 | elFormExpose?.validateField(fields, (isValid) => { | ||
| 56 | if (isValid) { | ||
| 57 | resolve(isValid) | ||
| 58 | } else { | ||
| 59 | resolve(isValid) | ||
| 60 | } | ||
| 61 | }) | ||
| 62 | }) | ||
| 63 | } | ||
| 64 | |||
| 65 | const clearValidate = async (fields: string[] = []) => { // 表单验证 | ||
| 66 | const elFormExpose = await getElFormExpose() | ||
| 67 | elFormExpose?.clearValidate(fields) | ||
| 68 | } | ||
| 69 | |||
| 70 | const inoutFocus = async () => { | ||
| 71 | const inputEl: ComponentRef<typeof ElInput> = await getComponentExpose('field1') | ||
| 72 | inputEl?.focus() | ||
| 73 | } | ||
| 74 | |||
| 75 | const inoutValidation = async () => { | ||
| 76 | const formItem = await getFormItemExpose('field1') | ||
| 77 | const inputEl: ComponentRef<typeof ElInput> = await getComponentExpose('field1') | ||
| 78 | inputEl?.focus() | ||
| 79 | formItem?.validate('focus', (val: boolean) => { | ||
| 80 | console.log(val) | ||
| 81 | }) | ||
| 82 | } | ||
| 83 | |||
| 84 | const formValidate = (prop: FormItemProp, isValid: boolean, message: string) => { | ||
| 85 | // console.log(prop, isValid, message) | ||
| 86 | } | ||
| 87 | |||
| 88 | /** | ||
| 89 | * @param filterEmptyVal 是否过滤空值 | ||
| 90 | */ | ||
| 91 | const getData = (filterEmptyVal = true) => { | ||
| 92 | if (filterEmptyVal) { | ||
| 93 | return new Promise(async (resolve) => { | ||
| 94 | const resultForm = await getFormData(filterEmptyVal) | ||
| 95 | resolve(resultForm) | ||
| 96 | }) | ||
| 97 | } else { | ||
| 98 | return new Promise(async (resolve) => { | ||
| 99 | const resultForm = await getFormData(filterEmptyVal) | ||
| 100 | let res = getFormResult(resultForm, schemaData.value) | ||
| 101 | resolve(res) | ||
| 102 | }) | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | const setValue = async (param = {}, reset = false) => { | ||
| 107 | const elFormExpose = await getElFormExpose() | ||
| 108 | if (reset) { | ||
| 109 | elFormExpose?.resetFields() | ||
| 110 | } else { | ||
| 111 | setValues(param) | ||
| 112 | } | ||
| 113 | } | ||
| 114 | |||
| 115 | const submitForm = () => { | ||
| 116 | return new Promise(async (resolve) => { | ||
| 117 | const elFormExpose = await getElFormExpose() | ||
| 118 | scrollToError() | ||
| 119 | elFormExpose?.validate((isValid) => { | ||
| 120 | if (isValid) { | ||
| 121 | resolve(isValid) | ||
| 122 | } else { | ||
| 123 | resolve(isValid) | ||
| 124 | } | ||
| 125 | }) | ||
| 126 | }) | ||
| 127 | } | ||
| 128 | |||
| 129 | // 子组建暴露的方法 | ||
| 130 | defineExpose({ | ||
| 131 | formValidation, | ||
| 132 | getFormResult, | ||
| 133 | clearValidate, | ||
| 134 | setSchema, | ||
| 135 | submitForm, | ||
| 136 | getData, | ||
| 137 | setValue: setValue, | ||
| 138 | setValues: setValue, | ||
| 139 | formMethods | ||
| 140 | }) | ||
| 141 | </script> | ||
| 142 | |||
| 143 | <template> | ||
| 144 | <div> | ||
| 145 | <FormPlus :schema="schemaData" :disabled="disabled" :model="formModel" :isCol="true" :labelPosition="labelPosition" | ||
| 146 | @register="formRegister" @validate="formValidate" /> | ||
| 147 | </div> | ||
| 148 | </template> | ||
| 149 | |||
| 150 | <style lang="scss" scoped> | ||
| 151 | .el-button { | ||
| 152 | margin-top: 10px; | ||
| 153 | } | ||
| 154 | </style> |
src/components/FormItem/index.ts
0 → 100644
src/components/FormPlus/index.ts
0 → 100644
| 1 | import FormPlus from './src/FormPlus.vue' | ||
| 2 | import type { FormSchema, FormSetProps } from './src/types' | ||
| 3 | export type { | ||
| 4 | ComponentNameEnum, | ||
| 5 | ComponentName, | ||
| 6 | InputComponentProps, | ||
| 7 | AutocompleteComponentProps, | ||
| 8 | InputNumberComponentProps, | ||
| 9 | SelectOption, | ||
| 10 | SelectComponentProps, | ||
| 11 | SelectV2ComponentProps, | ||
| 12 | CascaderComponentProps, | ||
| 13 | SwitchComponentProps, | ||
| 14 | RateComponentProps, | ||
| 15 | ColorPickerComponentProps, | ||
| 16 | TransferComponentProps, | ||
| 17 | RadioOption, | ||
| 18 | RadioGroupComponentProps, | ||
| 19 | RadioButtonComponentProps, | ||
| 20 | CheckboxOption, | ||
| 21 | CheckboxGroupComponentProps, | ||
| 22 | DividerComponentProps, | ||
| 23 | DatePickerComponentProps, | ||
| 24 | DateTimePickerComponentProps, | ||
| 25 | TimePickerComponentProps, | ||
| 26 | TimeSelectComponentProps, | ||
| 27 | ColProps, | ||
| 28 | FormSetProps, | ||
| 29 | FormItemProps, | ||
| 30 | FormSchema, | ||
| 31 | FormProps, | ||
| 32 | PlaceholderModel, | ||
| 33 | InputPasswordComponentProps, | ||
| 34 | TreeSelectComponentProps | ||
| 35 | } from './src/types' | ||
| 36 | |||
| 37 | export interface FormExpose { | ||
| 38 | setValues: (data: any) => void | ||
| 39 | setProps: (props: any) => void | ||
| 40 | delSchema: (field: string,deleteField:boolean) => void | ||
| 41 | addSchema: (formSchema: FormSchema, index?: number) => void | ||
| 42 | setSchema: (schemaProps: FormSetProps[]) => void | ||
| 43 | formModel: any | ||
| 44 | getComponentExpose: (field: string) => any | ||
| 45 | getFormItemExpose: (field: string) => any | ||
| 46 | } | ||
| 47 | |||
| 48 | export { FormPlus } |
src/components/FormPlus/src/FormPlus.vue
0 → 100644
This diff is collapsed.
Click to expand it.
| 1 | |||
| 2 | interface RenderHeader { | ||
| 3 | label:string // 列label | ||
| 4 | labelClass?:string | ||
| 5 | style?:string | ||
| 6 | iconProps?:IconProps | ||
| 7 | popoverProps:PopoverProps | ||
| 8 | } | ||
| 9 | interface IconProps { | ||
| 10 | iconClass?:string // 图标类名 | ||
| 11 | icon:string // 图标名称 | ||
| 12 | } | ||
| 13 | interface PopoverProps { | ||
| 14 | visible?:boolean | ||
| 15 | popoverContent:string // 提示内容 | ||
| 16 | popoverWidth?:number // 提示框宽度 | ||
| 17 | popoverTrigger? : 'click' | 'focus' | 'hover' | 'contextmenu' // 触发方式 | ||
| 18 | popoverTitle?:string // 提示标题 | ||
| 19 | } | ||
| 20 | |||
| 21 | export const useRenderAmountInput = () => { | ||
| 22 | /** | ||
| 23 | * 金额input渲染函数 | ||
| 24 | * @param param | ||
| 25 | * @param isDetail 是否详情 | ||
| 26 | * @returns | ||
| 27 | */ | ||
| 28 | const renderAmountInput = (param:RenderHeader,isDetail?:boolean) => { | ||
| 29 | return ( | ||
| 30 | <> | ||
| 31 | |||
| 32 | </> | ||
| 33 | ); | ||
| 34 | } | ||
| 35 | |||
| 36 | return { | ||
| 37 | renderAmountInput | ||
| 38 | } | ||
| 39 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
| 1 | import { FormSchema, ComponentNameEnum, CheckboxGroupComponentProps } from '../types' | ||
| 2 | import { ElCheckbox, ElCheckboxButton } from 'element-plus' | ||
| 3 | import { defineComponent } from 'vue' | ||
| 4 | |||
| 5 | export const useRenderCheckbox = () => { | ||
| 6 | const renderCheckboxOptions = (item: FormSchema) => { | ||
| 7 | // 如果有别名,就取别名 | ||
| 8 | const componentProps = item?.componentProps as CheckboxGroupComponentProps | ||
| 9 | const valueAlias = componentProps?.props?.value || 'value' | ||
| 10 | const labelAlias = componentProps?.props?.label || 'label' | ||
| 11 | const disabledAlias = componentProps?.props?.disabled || 'disabled' | ||
| 12 | const Com = ( | ||
| 13 | item.component === ComponentNameEnum.CHECKBOX_GROUP ? ElCheckbox : ElCheckboxButton | ||
| 14 | ) as ReturnType<typeof defineComponent> | ||
| 15 | return componentProps?.options?.map((option) => { | ||
| 16 | const { value, ...other } = option | ||
| 17 | return ( | ||
| 18 | <Com | ||
| 19 | {...other} | ||
| 20 | disabled={option[disabledAlias || 'disabled']} | ||
| 21 | label={option[valueAlias || 'value']} | ||
| 22 | > | ||
| 23 | {option[labelAlias || 'label']} | ||
| 24 | </Com> | ||
| 25 | ) | ||
| 26 | }) | ||
| 27 | } | ||
| 28 | |||
| 29 | return { | ||
| 30 | renderCheckboxOptions | ||
| 31 | } | ||
| 32 | } |
| 1 | import { FormSchema, ComponentNameEnum, RadioGroupComponentProps } from '../types' | ||
| 2 | import { ElRadio, ElRadioButton, ElIcon } from 'element-plus' | ||
| 3 | import { defineComponent } from 'vue' | ||
| 4 | import { Check } from "@element-plus/icons-vue" | ||
| 5 | import '../styles/radio.scss' | ||
| 6 | |||
| 7 | export const useRenderRadio = () => { | ||
| 8 | const renderRadioOptions = (item: FormSchema, formModel: any) => { | ||
| 9 | // 如果有别名,就取别名 | ||
| 10 | const { field } = item | ||
| 11 | const componentProps = item?.componentProps as RadioGroupComponentProps | ||
| 12 | const valueAlias = componentProps?.props?.value || 'value' | ||
| 13 | const labelAlias = componentProps?.props?.label || 'label' | ||
| 14 | const disabledAlias = componentProps?.props?.disabled || 'disabled' | ||
| 15 | const Com = ( | ||
| 16 | item.component === ComponentNameEnum.RADIO_GROUP ? ElRadio : ElRadioButton | ||
| 17 | ) as ReturnType<typeof defineComponent> | ||
| 18 | return componentProps?.options?.map((option) => { | ||
| 19 | const { value, ...other } = option | ||
| 20 | return ( | ||
| 21 | <div class={other.border ? 'radio_panel' : ''}> | ||
| 22 | <Com | ||
| 23 | {...other} | ||
| 24 | disabled={option[disabledAlias || 'disabled']} | ||
| 25 | label={option[valueAlias || 'value']} | ||
| 26 | style='margin-right:8px' | ||
| 27 | > | ||
| 28 | {option[labelAlias || 'label']} | ||
| 29 | { | ||
| 30 | other.border ? | ||
| 31 | (<ElIcon class={ formModel.value[field] == value ? 'active corner_mark':'corner_mark' }> | ||
| 32 | <Check></Check> | ||
| 33 | </ElIcon>) : undefined | ||
| 34 | } | ||
| 35 | </Com> | ||
| 36 | </div> | ||
| 37 | ) | ||
| 38 | }) | ||
| 39 | } | ||
| 40 | |||
| 41 | return { | ||
| 42 | renderRadioOptions | ||
| 43 | } | ||
| 44 | } |
| 1 | import { ElOption, ElOptionGroup } from 'element-plus' | ||
| 2 | import { FormSchema, SelectComponentProps, SelectOption } from '../types' | ||
| 3 | |||
| 4 | export const useRenderSelect = () => { | ||
| 5 | // 渲染 select options | ||
| 6 | const renderSelectOptions = (item: FormSchema) => { | ||
| 7 | const componentsProps = item?.componentProps as SelectComponentProps | ||
| 8 | const optionGroupDefaultSlot = componentsProps?.slots?.optionGroupDefault | ||
| 9 | // 如果有别名,就取别名 | ||
| 10 | const labelAlias = componentsProps?.props?.label | ||
| 11 | const keyAlias = componentsProps?.props?.key | ||
| 12 | return componentsProps?.options?.map((option) => { | ||
| 13 | if (option?.options?.length) { | ||
| 14 | return optionGroupDefaultSlot ? ( | ||
| 15 | optionGroupDefaultSlot(option) | ||
| 16 | ) : ( | ||
| 17 | <ElOptionGroup label={option[labelAlias || 'label']} key={option[keyAlias || 'key']}> | ||
| 18 | {{ | ||
| 19 | default: () => | ||
| 20 | option?.options?.map((v) => { | ||
| 21 | return renderSelectOptionItem(item, v) | ||
| 22 | }) | ||
| 23 | }} | ||
| 24 | </ElOptionGroup> | ||
| 25 | ) | ||
| 26 | } else { | ||
| 27 | return renderSelectOptionItem(item, option) | ||
| 28 | } | ||
| 29 | }) | ||
| 30 | } | ||
| 31 | |||
| 32 | // 渲染 select option item | ||
| 33 | const renderSelectOptionItem = (item: FormSchema, option: SelectOption) => { | ||
| 34 | // 如果有别名,就取别名 | ||
| 35 | const componentsProps = item.componentProps as SelectComponentProps | ||
| 36 | const labelAlias = componentsProps?.props?.label | ||
| 37 | const valueAlias = componentsProps?.props?.value | ||
| 38 | const keyAlias = componentsProps?.props?.key | ||
| 39 | const optionDefaultSlot = componentsProps.slots?.optionDefault | ||
| 40 | |||
| 41 | return ( | ||
| 42 | <ElOption | ||
| 43 | {...option} | ||
| 44 | key={option[keyAlias || 'key']} | ||
| 45 | label={option[labelAlias || 'label']} | ||
| 46 | value={option[valueAlias || 'value']} | ||
| 47 | disabled={option.disabled} | ||
| 48 | > | ||
| 49 | {{ | ||
| 50 | default: () => (optionDefaultSlot ? optionDefaultSlot(option) : undefined) | ||
| 51 | }} | ||
| 52 | </ElOption> | ||
| 53 | ) | ||
| 54 | } | ||
| 55 | |||
| 56 | return { | ||
| 57 | renderSelectOptions | ||
| 58 | } | ||
| 59 | } |
| 1 | import type { Component } from 'vue' | ||
| 2 | import { | ||
| 3 | ElCascader, | ||
| 4 | ElCheckboxGroup, | ||
| 5 | ElColorPicker, | ||
| 6 | ElDatePicker, | ||
| 7 | ElInput, | ||
| 8 | ElInputNumber, | ||
| 9 | ElRadioGroup, | ||
| 10 | ElRate, | ||
| 11 | ElSelect, | ||
| 12 | ElSelectV2, | ||
| 13 | ElSlider, | ||
| 14 | ElSwitch, | ||
| 15 | ElTimePicker, | ||
| 16 | ElTimeSelect, | ||
| 17 | ElTransfer, | ||
| 18 | ElAutocomplete, | ||
| 19 | ElDivider, | ||
| 20 | ElTreeSelect, | ||
| 21 | ElUpload | ||
| 22 | } from 'element-plus' | ||
| 23 | // import { InputPassword } from '@/components/InputPassword' | ||
| 24 | // import { Editor } from '@/components/Editor' | ||
| 25 | // import { JsonEditor } from '@/components/JsonEditor' | ||
| 26 | // import { IconPicker } from '@/components/IconPicker' | ||
| 27 | import { ComponentName } from '../types' | ||
| 28 | import { Recordable } from '@/types/global' | ||
| 29 | |||
| 30 | const componentMap: Recordable<Component, ComponentName> = { | ||
| 31 | RadioGroup: ElRadioGroup, | ||
| 32 | RadioButton: ElRadioGroup, | ||
| 33 | CheckboxGroup: ElCheckboxGroup, | ||
| 34 | CheckboxButton: ElCheckboxGroup, | ||
| 35 | Input: ElInput, | ||
| 36 | Autocomplete: ElAutocomplete, | ||
| 37 | InputNumber: ElInputNumber, | ||
| 38 | Select: ElSelect, | ||
| 39 | Cascader: ElCascader, | ||
| 40 | Switch: ElSwitch, | ||
| 41 | Slider: ElSlider, | ||
| 42 | TimePicker: ElTimePicker, | ||
| 43 | DatePicker: ElDatePicker, | ||
| 44 | Rate: ElRate, | ||
| 45 | ColorPicker: ElColorPicker, | ||
| 46 | Transfer: ElTransfer, | ||
| 47 | Divider: ElDivider, | ||
| 48 | TimeSelect: ElTimeSelect, | ||
| 49 | SelectV2: ElSelectV2, | ||
| 50 | TreeSelect: ElTreeSelect, | ||
| 51 | Upload: ElUpload, | ||
| 52 | // InputPassword: InputPassword, | ||
| 53 | // Editor: Editor, | ||
| 54 | // JsonEditor: JsonEditor, | ||
| 55 | // IconPicker: IconPicker | ||
| 56 | } | ||
| 57 | |||
| 58 | export { componentMap } |
src/components/FormPlus/src/helper/index.ts
0 → 100644
| 1 | |||
| 2 | import { firstUpperCase, humpToDash } from '@/utils/common' | ||
| 3 | import { PlaceholderModel, FormSchema, ComponentNameEnum, ColProps } from '../types' | ||
| 4 | import { set, get } from 'lodash-es' | ||
| 5 | import { Recordable } from '@/types/global' | ||
| 6 | |||
| 7 | /** | ||
| 8 | * | ||
| 9 | * @param schema 对应组件数据 | ||
| 10 | * @returns 返回提示信息对象 | ||
| 11 | * @description 用于自动设置placeholder | ||
| 12 | */ | ||
| 13 | export const setTextPlaceholder = (schema: FormSchema): PlaceholderModel => { | ||
| 14 | const textMap = [ | ||
| 15 | ComponentNameEnum.INPUT, | ||
| 16 | ComponentNameEnum.AUTOCOMPLETE, | ||
| 17 | ComponentNameEnum.INPUT_NUMBER, | ||
| 18 | ComponentNameEnum.INPUT_PASSWORD | ||
| 19 | ] | ||
| 20 | const selectMap = [ | ||
| 21 | ComponentNameEnum.SELECT, | ||
| 22 | ComponentNameEnum.TIME_PICKER, | ||
| 23 | ComponentNameEnum.DATE_PICKER, | ||
| 24 | ComponentNameEnum.TIME_SELECT, | ||
| 25 | ComponentNameEnum.SELECT_V2 | ||
| 26 | ] | ||
| 27 | if (textMap.includes(schema?.component as ComponentNameEnum)) { | ||
| 28 | return { | ||
| 29 | placeholder: '请输入' | ||
| 30 | } | ||
| 31 | } | ||
| 32 | if (selectMap.includes(schema?.component as ComponentNameEnum)) { | ||
| 33 | // 一些范围选择器 | ||
| 34 | const twoTextMap = ['datetimerange', 'daterange', 'monthrange', 'datetimerange', 'daterange'] | ||
| 35 | if ( | ||
| 36 | twoTextMap.includes( | ||
| 37 | ((schema?.componentProps as any)?.type || | ||
| 38 | (schema?.componentProps as any)?.isRange) as string | ||
| 39 | ) | ||
| 40 | ) { | ||
| 41 | return { | ||
| 42 | startPlaceholder: '开始时间', | ||
| 43 | endPlaceholder: '结束时间', | ||
| 44 | rangeSeparator: '-' | ||
| 45 | } | ||
| 46 | } else { | ||
| 47 | return { | ||
| 48 | placeholder: '请选择' | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| 52 | return {} | ||
| 53 | } | ||
| 54 | |||
| 55 | /** | ||
| 56 | * | ||
| 57 | * @param col 内置栅格 | ||
| 58 | * @returns 返回栅格属性 | ||
| 59 | * @description 合并传入进来的栅格属性 | ||
| 60 | */ | ||
| 61 | export const setGridProp = (col: ColProps = {}): ColProps => { | ||
| 62 | const colProps: ColProps = { | ||
| 63 | // 如果有span,代表用户优先级更高,所以不需要默认栅格 | ||
| 64 | ...(col.span | ||
| 65 | ? {} | ||
| 66 | : { | ||
| 67 | xs: 24, | ||
| 68 | sm: 12, | ||
| 69 | md: 12, | ||
| 70 | lg: 12, | ||
| 71 | xl: 12 | ||
| 72 | }), | ||
| 73 | ...col | ||
| 74 | } | ||
| 75 | return colProps | ||
| 76 | } | ||
| 77 | |||
| 78 | /** | ||
| 79 | * | ||
| 80 | * @param item 传入的组件属性 | ||
| 81 | * @param props 传入表单所有属性 | ||
| 82 | * @returns 默认添加 clearable 属性 | ||
| 83 | */ | ||
| 84 | export const setComponentProps = (item: FormSchema,props:any): Recordable => { | ||
| 85 | // const notNeedClearable = ['ColorPicker'] | ||
| 86 | // 拆分事件并组合 | ||
| 87 | const onEvents = (item?.componentProps as any)?.on || {} | ||
| 88 | const newOnEvents: Recordable = {} | ||
| 89 | |||
| 90 | for (const key in onEvents) { | ||
| 91 | if (onEvents[key]) { | ||
| 92 | newOnEvents[`on${firstUpperCase(key)}`] = (...args: any[]) => { | ||
| 93 | onEvents[key](...args) | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | |||
| 98 | const componentProps: Recordable = { | ||
| 99 | clearable: true, // 默认全局开启清空属性 | ||
| 100 | ...item.componentProps, | ||
| 101 | ...newOnEvents | ||
| 102 | } | ||
| 103 | // 需要删除额外的属性 | ||
| 104 | if (componentProps.slots) { | ||
| 105 | delete componentProps.slots | ||
| 106 | } | ||
| 107 | if (componentProps.on) { | ||
| 108 | delete componentProps.on | ||
| 109 | } | ||
| 110 | if ((item.component === 'Select' || item.component === 'Cascader' || item.component === 'TreeSelect') && (componentProps.filterable === undefined)) { | ||
| 111 | componentProps.filterable = true | ||
| 112 | } | ||
| 113 | if ((item.component === 'Select' || item.component === 'TreeSelect') && (componentProps.fitInputWidth === undefined)) { | ||
| 114 | componentProps.fitInputWidth = true | ||
| 115 | } | ||
| 116 | if ((item.component === 'InputNumber') && (componentProps.controls === undefined)) { // 默认全局取消加减号按钮 | ||
| 117 | componentProps.controls = false | ||
| 118 | } | ||
| 119 | if ((item.component === 'InputNumber') && componentProps.precision === undefined) { // 默认全局2位小数 | ||
| 120 | componentProps.precision = (props.disabled || componentProps.disabled) ? 3 : 2 | ||
| 121 | } | ||
| 122 | if ((item.component === 'InputNumber') && componentProps.min === undefined) { // 默认默认最小值0 | ||
| 123 | componentProps.min = 0 | ||
| 124 | } | ||
| 125 | // if ((item.component === 'Input') && componentProps.formatter === undefined) { // 默认添加过滤前后空格 | ||
| 126 | // componentProps.formatter = (value:string) => value.trim() | ||
| 127 | // } | ||
| 128 | return componentProps | ||
| 129 | } | ||
| 130 | |||
| 131 | /** | ||
| 132 | * | ||
| 133 | * @param formModel 表单数据 | ||
| 134 | * @param slotsProps 插槽属性 | ||
| 135 | */ | ||
| 136 | export const setItemComponentSlots = (slotsProps: Recordable = {}): Recordable => { | ||
| 137 | const slotObj: Recordable = {} | ||
| 138 | for (const key in slotsProps) { | ||
| 139 | if (slotsProps[key]) { | ||
| 140 | if (typeof slotsProps[key] == 'function') { | ||
| 141 | slotObj[humpToDash(key)] = (...args: any[]) => { | ||
| 142 | return slotsProps[key]?.(...args) | ||
| 143 | } | ||
| 144 | } else { | ||
| 145 | slotObj[humpToDash(key)] = () => { | ||
| 146 | return slotsProps[key] | ||
| 147 | } | ||
| 148 | } | ||
| 149 | } | ||
| 150 | } | ||
| 151 | return slotObj | ||
| 152 | } | ||
| 153 | |||
| 154 | /** | ||
| 155 | * | ||
| 156 | * @param schema Form表单结构化数组 | ||
| 157 | * @param formModel FormMoel | ||
| 158 | * @returns FormMoel | ||
| 159 | * @description 生成对应的formModel | ||
| 160 | */ | ||
| 161 | export const initModel = (schema: FormSchema[], formModel: Recordable) => { | ||
| 162 | const model: Recordable = { ...formModel } | ||
| 163 | schema.map((v) => { | ||
| 164 | if (v.remove) { | ||
| 165 | delete model[v.field] | ||
| 166 | } else if (v.component !== 'Divider') { | ||
| 167 | // const hasField = Reflect.has(model, v.field) | ||
| 168 | const hasField = get(model, v.field) | ||
| 169 | // 如果先前已经有值存在,则不进行重新赋值,而是采用现有的值 | ||
| 170 | set( | ||
| 171 | model, | ||
| 172 | v.field, | ||
| 173 | hasField !== void 0 ? get(model, v.field) : v.value !== void 0 ? v.value : undefined | ||
| 174 | ) | ||
| 175 | // model[v.field] = hasField ? model[v.field] : v.value !== void 0 ? v.value : undefined | ||
| 176 | } | ||
| 177 | }) | ||
| 178 | return model | ||
| 179 | } |
| 1 | .radio_panel { | ||
| 2 | .el-radio { | ||
| 3 | &.is-bordered { | ||
| 4 | padding: 0; | ||
| 5 | position: relative; | ||
| 6 | border-radius:2px; | ||
| 7 | .el-radio__input { | ||
| 8 | position: absolute; | ||
| 9 | opacity: 0; | ||
| 10 | } | ||
| 11 | |||
| 12 | .el-radio__label { | ||
| 13 | padding: 0 15px; | ||
| 14 | font-size: 14px; | ||
| 15 | position: relative; | ||
| 16 | |||
| 17 | .corner_mark { | ||
| 18 | position: absolute; | ||
| 19 | right: 0; | ||
| 20 | bottom: -4px; | ||
| 21 | width: 18px; | ||
| 22 | height: 18px; | ||
| 23 | color: #fff; | ||
| 24 | |||
| 25 | svg { | ||
| 26 | width: 9px; | ||
| 27 | height: 9px; | ||
| 28 | position: absolute; | ||
| 29 | right: 0; | ||
| 30 | bottom: 2px; | ||
| 31 | z-index: 1; | ||
| 32 | } | ||
| 33 | |||
| 34 | &.active { | ||
| 35 | &::after { | ||
| 36 | content: ""; | ||
| 37 | position: absolute; | ||
| 38 | border: 9px solid var(--el-color-primary); | ||
| 39 | border-top-color: transparent; | ||
| 40 | border-left-color: transparent; | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | } | ||
| 45 | |||
| 46 | .el-radio__input.is-checked+.el-radio__label { | ||
| 47 | color: var(--el-color-primary); | ||
| 48 | } | ||
| 49 | } | ||
| 50 | } | ||
| 51 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/components/FormPlus/src/types/index.ts
0 → 100644
This diff is collapsed.
Click to expand it.
src/components/Retrievepassword/index.vue
0 → 100644
| 1 | <template> | ||
| 2 | <div> | ||
| 3 | <DialogPlus modal-class="auth-user" append-to-body v-model="dialogVisible" @open="openedDialog" width="460px" | ||
| 4 | maxHeight="250px" :close-on-click-modal="false" title="用户身份认证"> | ||
| 5 | <div class="select-tenant"> | ||
| 6 | <el-form v-show="formType == 'validate'" label-position="top" ref="registerFormRef" :model="registerForm" | ||
| 7 | :rules="registerRules" class="login-form" auto-complete="on"> | ||
| 8 | <el-form-item prop="logonUser" label="账号"> | ||
| 9 | <el-input v-model.trim="registerForm.logonUser" :disabled="isModifypassword" placeholder="请输入账号" text tabindex="1" autocomplete="on"> | ||
| 10 | </el-input> | ||
| 11 | </el-form-item> | ||
| 12 | <el-form-item prop="mobileNo" label="手机号"> | ||
| 13 | <el-input v-model.trim="registerForm.mobileNo" :disabled="isModifypassword" placeholder="请输入手机号" text tabindex="1" autocomplete="on"> | ||
| 14 | </el-input> | ||
| 15 | </el-form-item> | ||
| 16 | <el-form-item prop="validateCode" label="图形验证码"> | ||
| 17 | <el-input class="captcha" v-model.trim="registerForm.validateCode" placeholder="请输入图形验证码" tabindex="2" | ||
| 18 | autocomplete="on"> | ||
| 19 | <template #append> | ||
| 20 | <img class="h-26px" :src="imgCaptchaBase64" @click="refreshPictureCode" /> | ||
| 21 | </template> | ||
| 22 | </el-input> | ||
| 23 | </el-form-item> | ||
| 24 | </el-form> | ||
| 25 | <el-form v-show="formType === 'reset'" label-position="top" ref="resetFormRef" :model="resetForm" :rules="resetRules" | ||
| 26 | autocomplete="off" class="login-form" auto-complete="on"> | ||
| 27 | <el-form-item prop="smsCode" autocomplete="off" label="短信验证码"> | ||
| 28 | <el-input v-model.trim="resetForm.smsCode" autocomplete="off" name="captcha-unique123" | ||
| 29 | :placeholder="`请输入${registerForm.mobileNo.substring(0, 3)}****${registerForm.mobileNo.substring(7)}收到的短信验证码`"> | ||
| 30 | <template #append> | ||
| 31 | <span>{{ `${timeLeft}s` }}</span> | ||
| 32 | </template> | ||
| 33 | </el-input> | ||
| 34 | <div class="code-desc"> | ||
| 35 | <span>若手机未收到验证码,请</span> | ||
| 36 | <el-button link type="primary" size="small" @click="recertification" | ||
| 37 | style="padding: 0px;vertical-align: baseline;">重新认证</el-button> | ||
| 38 | </div> | ||
| 39 | </el-form-item> | ||
| 40 | <el-form-item prop="pwd" label="新密码" :error="errorPsw"> | ||
| 41 | <!-- <el-input v-model.trim="resetForm.pwd" type="password" placeholder="请输入新密码" show-password autocomplete="off" | ||
| 42 | autocomplete="new-password"> | ||
| 43 | </el-input> --> | ||
| 44 | <PasswordStrengthMeter v-model.trim="password" placeholder="请输入新密码" @change="changePwd" style="width: 100%;"></PasswordStrengthMeter> | ||
| 45 | </el-form-item> | ||
| 46 | <el-form-item prop="checkPwd" label="确认新密码"> | ||
| 47 | <el-input v-model.trim="resetForm.checkPwd" type="password" placeholder="请再次输入新密码" show-password | ||
| 48 | autocomplete="new-password"> | ||
| 49 | </el-input> | ||
| 50 | </el-form-item> | ||
| 51 | </el-form> | ||
| 52 | </div> | ||
| 53 | <template #footer> | ||
| 54 | <el-button @click="closeDialog">取 消</el-button> | ||
| 55 | <el-button v-show="formType === 'validate'" :loading="sendCodeLoading" type="primary" | ||
| 56 | @click="handleSendCaptcha">发送验证码至手机</el-button> | ||
| 57 | <el-button v-show="formType === 'reset' && timeLeft > 0" :loading="saveLoading" type="primary" | ||
| 58 | :disabled="!resetForm.smsCode || !resetForm.pwd || !resetForm.checkPwd" | ||
| 59 | @click.prevent="handleReset">修改密码</el-button> | ||
| 60 | <el-button v-show="formType === 'reset' && timeLeft == 0" :loading="saveLoading" type="primary" | ||
| 61 | @click.prevent="recertification">重新认证</el-button> | ||
| 62 | </template> | ||
| 63 | </DialogPlus> | ||
| 64 | </div> | ||
| 65 | </template> | ||
| 66 | |||
| 67 | <script lang="ts" setup> | ||
| 68 | import { useValidator } from '@/hooks/useValidator'; | ||
| 69 | import useCountdown from '@/hooks/useCountdown' | ||
| 70 | import type { FormRules } from 'element-plus' | ||
| 71 | import { propTypes } from '@/utils/propTypes' | ||
| 72 | import useIdaasStore from '@/store/modules/idaas'; | ||
| 73 | import { | ||
| 74 | getPictureCode | ||
| 75 | } from "@/api/modules/idaas" | ||
| 76 | import useLogin from '@/store/modules/login' | ||
| 77 | import PasswordStrengthMeter from '../PasswordStrengthMeter/index.vue' | ||
| 78 | import { autoSalt } from '@/utils/common'; | ||
| 79 | |||
| 80 | // const AsyncPasswordStrengthMeter = defineAsyncComponent(() => | ||
| 81 | // import('../PasswordStrengthMeter/index.vue') | ||
| 82 | // ); | ||
| 83 | |||
| 84 | const loginStore = useLogin() | ||
| 85 | const idaasStore = useIdaasStore() | ||
| 86 | const { required, phone } = useValidator(); | ||
| 87 | const { proxy } = getCurrentInstance() as any; | ||
| 88 | const mobileNo = computed(()=> idaasStore.idaasUserInfo.principal?.mobileNo || '') | ||
| 89 | const logonUser = computed(()=> idaasStore.idaasUserInfo.principal?.logonUser || '') | ||
| 90 | |||
| 91 | const props = defineProps({ | ||
| 92 | schemaInfo: { | ||
| 93 | type: Object as PropType<{ | ||
| 94 | visible: boolean; | ||
| 95 | }>, | ||
| 96 | default: () => { } | ||
| 97 | }, | ||
| 98 | operate:propTypes.string.def('modifypassword'), | ||
| 99 | }); | ||
| 100 | |||
| 101 | const isModifypassword = computed(() => props.operate === 'modifypassword') | ||
| 102 | |||
| 103 | const dialogVisible = computed({ | ||
| 104 | get: () => { | ||
| 105 | return props.schemaInfo.visible; | ||
| 106 | }, | ||
| 107 | set: (val) => { | ||
| 108 | props.schemaInfo.visible = val | ||
| 109 | } | ||
| 110 | }) | ||
| 111 | |||
| 112 | function openedDialog() { | ||
| 113 | recertification() | ||
| 114 | getPictureCodeInfo() | ||
| 115 | } | ||
| 116 | |||
| 117 | function closeDialog() { | ||
| 118 | dialogVisible.value = false | ||
| 119 | } | ||
| 120 | |||
| 121 | // 重新认证 | ||
| 122 | const recertification = () => { | ||
| 123 | formType.value = 'validate'; | ||
| 124 | clearForm() | ||
| 125 | reset() | ||
| 126 | } | ||
| 127 | |||
| 128 | function clearForm() { | ||
| 129 | let _mobileNo = isModifypassword.value ? mobileNo.value : '' | ||
| 130 | let _logonUser = isModifypassword.value ? logonUser.value : '' | ||
| 131 | password.value = '' | ||
| 132 | resetForm.value = { | ||
| 133 | smsCode: '', | ||
| 134 | pwd: '', | ||
| 135 | checkPwd: '' | ||
| 136 | } | ||
| 137 | registerForm.value = { | ||
| 138 | logonUser: _logonUser, | ||
| 139 | mobileNo: _mobileNo, | ||
| 140 | validateCode: '' | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | // ! 修改密码逻辑 | ||
| 145 | const formType = ref('validate') | ||
| 146 | const resetFormRef = ref() | ||
| 147 | const registerFormRef = ref() | ||
| 148 | |||
| 149 | |||
| 150 | |||
| 151 | const registerRules = ref<FormRules>({ | ||
| 152 | logonUser: [ | ||
| 153 | { required: true, trigger: 'blur', message: '请输入账号' }, | ||
| 154 | ], | ||
| 155 | mobileNo: [ | ||
| 156 | required('请输入手机号'), phone() | ||
| 157 | ], | ||
| 158 | validateCode: [ | ||
| 159 | { required: true, trigger: 'blur', message: '请输入图形验证码' }, | ||
| 160 | ] | ||
| 161 | }) | ||
| 162 | const registerForm = ref({ | ||
| 163 | logonUser: '', | ||
| 164 | mobileNo: '', | ||
| 165 | validateCode: '' | ||
| 166 | }) | ||
| 167 | |||
| 168 | function changePwd() { | ||
| 169 | resetForm.value.pwd = password.value | ||
| 170 | } | ||
| 171 | |||
| 172 | |||
| 173 | const errorPsw = computed(() => loginStore.firstUnmetRequirement ? `需要${loginStore.firstUnmetRequirement}` : null) | ||
| 174 | const password = ref('') | ||
| 175 | /** | ||
| 176 | * 校验新密码 | ||
| 177 | */ | ||
| 178 | function validatorPassword(rule, value, callback) { | ||
| 179 | console.log(value,loginStore.firstUnmetRequirement); | ||
| 180 | if (loginStore.firstUnmetRequirement) { | ||
| 181 | callback(new Error(`需要${loginStore.firstUnmetRequirement}`)) | ||
| 182 | } else { | ||
| 183 | callback(); | ||
| 184 | } | ||
| 185 | } | ||
| 186 | |||
| 187 | /** | ||
| 188 | * 校验确认密码 | ||
| 189 | */ | ||
| 190 | function validatorConfirmpwd(rule, value, callback) { | ||
| 191 | if (value != resetForm.value.pwd) { | ||
| 192 | callback(new Error('密码不一致,请重新输入')) | ||
| 193 | } else { | ||
| 194 | callback(); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 198 | const resetRules = ref<FormRules>({ | ||
| 199 | smsCode: [ | ||
| 200 | // 不显示验证信息了,只有填写完整,保存按钮才会被使用。 | ||
| 201 | // {required: true, trigger: 'blur', message: '请输入收到的短信验证码' }, | ||
| 202 | ], | ||
| 203 | pwd: [ | ||
| 204 | { | ||
| 205 | validator: validatorPassword, | ||
| 206 | trigger: [] | ||
| 207 | } | ||
| 208 | ], | ||
| 209 | checkPwd: [ | ||
| 210 | { | ||
| 211 | validator: validatorConfirmpwd, | ||
| 212 | trigger: ['change', 'blur'] | ||
| 213 | } | ||
| 214 | ] | ||
| 215 | }) | ||
| 216 | const resetForm = ref({ | ||
| 217 | smsCode: '', | ||
| 218 | pwd: '', | ||
| 219 | checkPwd: '' | ||
| 220 | }) | ||
| 221 | |||
| 222 | |||
| 223 | |||
| 224 | |||
| 225 | let promise: any = ref(null); | ||
| 226 | /** 图形验证码图片。 */ | ||
| 227 | const imgCaptchaBase64 = ref(''); | ||
| 228 | /** 图形验证码guid */ | ||
| 229 | const validateCodeGuid = ref(''); | ||
| 230 | |||
| 231 | /** 发送短信验证码的loading */ | ||
| 232 | const sendCodeLoading = ref(false); | ||
| 233 | /** 重置密码保存的loading */ | ||
| 234 | const saveLoading = ref(false); | ||
| 235 | |||
| 236 | |||
| 237 | const { timeLeft, minutes, seconds, start, stop, reset } = useCountdown(300); | ||
| 238 | watchEffect(() => { | ||
| 239 | console.log(timeLeft.value, 'timeLeft'); | ||
| 240 | if (timeLeft.value == 0) { | ||
| 241 | stop() | ||
| 242 | } | ||
| 243 | }) | ||
| 244 | |||
| 245 | |||
| 246 | const refreshPictureCode = () => { | ||
| 247 | if (promise.value) { | ||
| 248 | return; | ||
| 249 | } | ||
| 250 | getPictureCodeInfo() | ||
| 251 | } | ||
| 252 | |||
| 253 | function getPictureCodeInfo() { | ||
| 254 | promise.value = getPictureCode().then((res: any) => { | ||
| 255 | promise.value = null; | ||
| 256 | if (res.data.code == proxy.$passCode) { | ||
| 257 | imgCaptchaBase64.value = res.data.data?.imageBase64 || ""; | ||
| 258 | validateCodeGuid.value = res.data.data?.guid || ""; | ||
| 259 | } | ||
| 260 | }); | ||
| 261 | } | ||
| 262 | |||
| 263 | const handleSendCaptcha = () => { | ||
| 264 | registerFormRef.value && registerFormRef.value.validate((valid) => { | ||
| 265 | if (valid) { | ||
| 266 | sendCodeLoading.value = true | ||
| 267 | let params: any = { ...registerForm.value }; | ||
| 268 | params.validateCodeGuid = validateCodeGuid.value; | ||
| 269 | idaasApi.sendCode(params).then((res: any) => { | ||
| 270 | sendCodeLoading.value = false; | ||
| 271 | if (res?.data.code == proxy.$passCode) { | ||
| 272 | proxy.$ElMessage.success('验证码发送成功!'); | ||
| 273 | loginStore.firstUnmetRequirement = '' | ||
| 274 | formType.value = 'reset'; | ||
| 275 | resetForm.value.smsCode = ''; | ||
| 276 | resetForm.value.pwd = ""; | ||
| 277 | resetForm.value.checkPwd = ""; | ||
| 278 | start() | ||
| 279 | } else { | ||
| 280 | refreshPictureCode(); | ||
| 281 | registerForm.value.validateCode = ""; | ||
| 282 | } | ||
| 283 | }); | ||
| 284 | } | ||
| 285 | }); | ||
| 286 | } | ||
| 287 | |||
| 288 | async function handleReset() { | ||
| 289 | let res = await resetFormRef.value?.validate() | ||
| 290 | if (!res) return | ||
| 291 | let params = Object.assign({}, resetForm.value, { mobileNo: registerForm.value.mobileNo, logonUser: registerForm.value.logonUser }); | ||
| 292 | params.pwd = autoSalt(params.pwd, false, false); | ||
| 293 | idaasApi.resetPwd(params).then((res: any) => { | ||
| 294 | if (res.data.code == proxy.$passCode) { | ||
| 295 | proxy.$ElMessage.success('密码重置成功'); | ||
| 296 | dialogVisible.value = false; | ||
| 297 | userApi.recordUpdateTime(registerForm.value.logonUser) | ||
| 298 | } | ||
| 299 | }); | ||
| 300 | } | ||
| 301 | |||
| 302 | |||
| 303 | </script> | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/hooks/base64url.ts
0 → 100644
| 1 | const lookup = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; | ||
| 2 | const reverseLookup = new Uint8Array(256); | ||
| 3 | |||
| 4 | // 初始化 reverseLookup 数组 | ||
| 5 | for (let i = 0; i < lookup.length; i++) { | ||
| 6 | reverseLookup[lookup.charCodeAt(i)] = i; | ||
| 7 | } | ||
| 8 | |||
| 9 | function decodeBase64url(base64url) { | ||
| 10 | var base64urlLength = base64url.length; | ||
| 11 | |||
| 12 | var placeHolderLength = base64url.charAt(base64urlLength - 2) === '=' ? 2 : base64url.charAt(base64urlLength - 1) === '=' ? 1 : 0; | ||
| 13 | var bufferLength = (base64urlLength * 3 / 4) - placeHolderLength; | ||
| 14 | |||
| 15 | var arrayBuffer = new ArrayBuffer(bufferLength); | ||
| 16 | var uint8Array = new Uint8Array(arrayBuffer); | ||
| 17 | |||
| 18 | var j = 0; | ||
| 19 | for (var i = 0; i < base64urlLength; i+=4) { | ||
| 20 | var tmp0 = reverseLookup[base64url.charCodeAt(i)]; | ||
| 21 | var tmp1 = reverseLookup[base64url.charCodeAt(i+1)]; | ||
| 22 | var tmp2 = reverseLookup[base64url.charCodeAt(i+2)]; | ||
| 23 | var tmp3 = reverseLookup[base64url.charCodeAt(i+3)]; | ||
| 24 | |||
| 25 | uint8Array[j++] = (tmp0 << 2) | (tmp1 >> 4); | ||
| 26 | uint8Array[j++] = ((tmp1 & 15) << 4) | (tmp2 >> 2); | ||
| 27 | uint8Array[j++] = ((tmp2 & 3) << 6) | (tmp3 & 63); | ||
| 28 | } | ||
| 29 | |||
| 30 | return arrayBuffer; | ||
| 31 | } | ||
| 32 | |||
| 33 | function encodeBase64url(arrayBuffer) { | ||
| 34 | var uint8Array = new Uint8Array(arrayBuffer); | ||
| 35 | var length = uint8Array.length; | ||
| 36 | var base64url = ""; | ||
| 37 | |||
| 38 | for (var i = 0; i < length; i+=3) { | ||
| 39 | base64url += lookup[uint8Array[i] >> 2]; | ||
| 40 | base64url += lookup[((uint8Array[i] & 3) << 4) | (uint8Array[i + 1] >> 4)]; | ||
| 41 | base64url += lookup[((uint8Array[i + 1] & 15) << 2) | (uint8Array[i + 2] >> 6)]; | ||
| 42 | base64url += lookup[uint8Array[i + 2] & 63]; | ||
| 43 | } | ||
| 44 | |||
| 45 | switch (length % 3) { | ||
| 46 | case 1: | ||
| 47 | base64url = base64url.substring(0, base64url.length - 2); | ||
| 48 | break; | ||
| 49 | case 2: | ||
| 50 | base64url = base64url.substring(0, base64url.length - 1); | ||
| 51 | break; | ||
| 52 | } | ||
| 53 | return base64url; | ||
| 54 | } | ||
| 55 | |||
| 56 | // 导出函数 | ||
| 57 | export default { decodeBase64url, encodeBase64url }; | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/hooks/useCountdown.ts
0 → 100644
| 1 | import { ref, computed } from 'vue'; | ||
| 2 | |||
| 3 | /** | ||
| 4 | * 创建一个倒计时的自定义组合函数。 | ||
| 5 | * | ||
| 6 | * @param {number} duration - 倒计时的初始秒数,默认为60秒。 | ||
| 7 | * @returns {object} 包含倒计时状态和控制方法的对象。 | ||
| 8 | */ | ||
| 9 | export default function useCountdown(duration = 60) { | ||
| 10 | // 使用 ref 创建响应式的倒计时剩余时间 | ||
| 11 | const timeLeft = ref(duration); | ||
| 12 | |||
| 13 | // 用于存储定时器 ID 的变量 | ||
| 14 | let intervalId = null; | ||
| 15 | |||
| 16 | // 计算属性,将剩余时间转换为分钟和秒数 | ||
| 17 | const minutes = computed(() => Math.floor(timeLeft.value / 60)); | ||
| 18 | const seconds = computed(() => | ||
| 19 | ('0' + (timeLeft.value % 60)).slice(-2) // 确保秒数始终显示两位数字 | ||
| 20 | ); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * 开始倒计时。 | ||
| 24 | */ | ||
| 25 | function start() { | ||
| 26 | if (!intervalId && timeLeft.value > 0) { | ||
| 27 | // 设置每秒减少一次剩余时间的定时器 | ||
| 28 | intervalId = setInterval(() => { | ||
| 29 | if (timeLeft.value <= 0) { | ||
| 30 | clearInterval(intervalId); // 当时间用完时清除定时器 | ||
| 31 | intervalId = null; | ||
| 32 | return; | ||
| 33 | } | ||
| 34 | timeLeft.value--; // 每秒减少一秒 | ||
| 35 | }, 1000); | ||
| 36 | } | ||
| 37 | } | ||
| 38 | |||
| 39 | /** | ||
| 40 | * 停止倒计时。 | ||
| 41 | */ | ||
| 42 | function stop() { | ||
| 43 | if (intervalId) { | ||
| 44 | clearInterval(intervalId); // 清除定时器以停止倒计时 | ||
| 45 | intervalId = null; | ||
| 46 | } | ||
| 47 | } | ||
| 48 | |||
| 49 | /** | ||
| 50 | * 重置倒计时到初始值或指定的新持续时间。 | ||
| 51 | * | ||
| 52 | * @param {number} [newDuration=duration] - 新的倒计时持续时间(秒),可选。 | ||
| 53 | */ | ||
| 54 | function reset(newDuration = duration) { | ||
| 55 | stop(); // 先停止当前倒计时 | ||
| 56 | timeLeft.value = newDuration; // 重置剩余时间为新的持续时间或默认持续时间 | ||
| 57 | } | ||
| 58 | |||
| 59 | // 返回包含倒计时状态和控制方法的对象 | ||
| 60 | return { | ||
| 61 | timeLeft, // 剩余时间(秒) | ||
| 62 | minutes, // 分钟 | ||
| 63 | seconds, // 秒 | ||
| 64 | start, // 开始 | ||
| 65 | stop, // 暂停 | ||
| 66 | reset // 重置 | ||
| 67 | }; | ||
| 68 | } | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/hooks/useForm.ts
0 → 100644
| 1 | import type { FormPlus, FormExpose } from '@/components/FormPlus' | ||
| 2 | import type { ElForm, ElFormItem } from 'element-plus' | ||
| 3 | import { ref, unref, nextTick } from 'vue' | ||
| 4 | import { FormSchema, FormSetProps, FormProps } from '@/components/FormPlus' | ||
| 5 | import { isEmptyVal, isObject } from '@/utils/common' | ||
| 6 | import { cloneDeep } from 'lodash-es' | ||
| 7 | import { ComponentRef } from '@/types/global' | ||
| 8 | |||
| 9 | export const useForm = () => { | ||
| 10 | // From实例 | ||
| 11 | const formRef = ref<typeof FormPlus & FormExpose>() | ||
| 12 | |||
| 13 | // ElForm实例 | ||
| 14 | const elFormRef = ref<ComponentRef<typeof ElForm>>() | ||
| 15 | |||
| 16 | /** | ||
| 17 | * @param ref Form实例 | ||
| 18 | * @param elRef ElForm实例 | ||
| 19 | */ | ||
| 20 | const register = (ref: typeof FormPlus & FormExpose, elRef: ComponentRef<typeof ElForm>) => { | ||
| 21 | formRef.value = ref | ||
| 22 | elFormRef.value = elRef | ||
| 23 | } | ||
| 24 | |||
| 25 | const getForm = async () => { | ||
| 26 | await nextTick() | ||
| 27 | const form = unref(formRef) | ||
| 28 | if (!form) { | ||
| 29 | console.error('The form is not registered. Please use the register method to register') | ||
| 30 | } | ||
| 31 | return form | ||
| 32 | } | ||
| 33 | |||
| 34 | /** | ||
| 35 | * !数据类型空值映射 | ||
| 36 | */ | ||
| 37 | const defaultEmptyValues = (fieldType:string | undefined):'' | any[] | null => { | ||
| 38 | let map = { | ||
| 39 | 'string':'', | ||
| 40 | 'number':'', | ||
| 41 | 'array':[], | ||
| 42 | 'date':null | ||
| 43 | } | ||
| 44 | return fieldType ? map[fieldType] : '' | ||
| 45 | } | ||
| 46 | // 一些内置的方法 | ||
| 47 | const methods = { | ||
| 48 | /** | ||
| 49 | * @description 设置form组件的props | ||
| 50 | * @param props form组件的props | ||
| 51 | */ | ||
| 52 | setProps: async (props: FormProps = {}) => { | ||
| 53 | const form = await getForm() | ||
| 54 | form?.setProps(props) | ||
| 55 | if (props.model) { | ||
| 56 | form?.setValues(props.model) | ||
| 57 | } | ||
| 58 | }, | ||
| 59 | |||
| 60 | /** | ||
| 61 | * @description 设置form的值 | ||
| 62 | * @param data 需要设置的数据 | ||
| 63 | */ | ||
| 64 | setValues: async (data: any) => { | ||
| 65 | const form = await getForm() | ||
| 66 | form?.setValues(data) | ||
| 67 | }, | ||
| 68 | |||
| 69 | /** | ||
| 70 | * @description 设置schema | ||
| 71 | * @param schemaProps 需要设置的schemaProps | ||
| 72 | */ | ||
| 73 | setSchema: async (schemaProps: FormSetProps[]) => { | ||
| 74 | const form = await getForm() | ||
| 75 | form?.setSchema(schemaProps) | ||
| 76 | }, | ||
| 77 | |||
| 78 | /** | ||
| 79 | * @description 新增schema | ||
| 80 | * @param formSchema 需要新增数据 | ||
| 81 | * @param index 在哪里新增 | ||
| 82 | */ | ||
| 83 | addSchema: async (formSchema: FormSchema, index?: number) => { | ||
| 84 | const form = await getForm() | ||
| 85 | form?.addSchema(formSchema, index) | ||
| 86 | }, | ||
| 87 | |||
| 88 | /** | ||
| 89 | * @description 新增schema | ||
| 90 | * @param formSchemas 需要新增数据 | ||
| 91 | * @param index 在哪里新增 | ||
| 92 | */ | ||
| 93 | addSchemas: async (formSchemas: FormSchema[], index?: number) => { | ||
| 94 | const form = await getForm() | ||
| 95 | form?.addSchemas(formSchemas, index) | ||
| 96 | }, | ||
| 97 | |||
| 98 | /** | ||
| 99 | * @description 删除schema | ||
| 100 | * @param field 删除哪个数据 | ||
| 101 | * @param deleteField 是否删除字段 | ||
| 102 | */ | ||
| 103 | delSchema: async (field: string,deleteField:boolean = true) => { | ||
| 104 | const form = await getForm() | ||
| 105 | form?.delSchema(field,deleteField) | ||
| 106 | }, | ||
| 107 | |||
| 108 | /** | ||
| 109 | * @description 获取表单数据 | ||
| 110 | * @returns form data | ||
| 111 | */ | ||
| 112 | getFormData: async (filterEmptyVal = true): Promise<any> => { | ||
| 113 | const form = await getForm() | ||
| 114 | const model = form?.formModel as any | ||
| 115 | if (filterEmptyVal) { | ||
| 116 | // 使用reduce过滤空值,并返回一个新对象 | ||
| 117 | return Object.keys(model).reduce((prev, next) => { | ||
| 118 | const value = model[next] | ||
| 119 | if (!isEmptyVal(value)) { | ||
| 120 | if (isObject(value)) { | ||
| 121 | if (Object.keys(value).length > 0) { | ||
| 122 | prev[next] = value | ||
| 123 | } | ||
| 124 | } else { | ||
| 125 | prev[next] = value | ||
| 126 | } | ||
| 127 | } | ||
| 128 | return prev | ||
| 129 | }, {}) as T | ||
| 130 | } else { | ||
| 131 | return model as T | ||
| 132 | } | ||
| 133 | }, | ||
| 134 | |||
| 135 | /** | ||
| 136 | * @description 获取表单组件的实例 | ||
| 137 | * @param field 表单项唯一标识 | ||
| 138 | * @returns component instance | ||
| 139 | */ | ||
| 140 | getComponentExpose: async (field: string) => { | ||
| 141 | const form = await getForm() | ||
| 142 | return form?.getComponentExpose(field) | ||
| 143 | }, | ||
| 144 | |||
| 145 | /** | ||
| 146 | * @description 获取formItem组件的实例 | ||
| 147 | * @param field 表单项唯一标识 | ||
| 148 | * @returns formItem instance | ||
| 149 | */ | ||
| 150 | getFormItemExpose: async (field: string) => { | ||
| 151 | const form = await getForm() | ||
| 152 | return form?.getFormItemExpose(field) as ComponentRef<typeof ElFormItem> | ||
| 153 | }, | ||
| 154 | |||
| 155 | /** | ||
| 156 | * @description 获取ElForm组件的实例 | ||
| 157 | * @returns ElForm instance | ||
| 158 | */ | ||
| 159 | getElFormExpose: async () => { | ||
| 160 | await getForm() | ||
| 161 | return unref(elFormRef) | ||
| 162 | }, | ||
| 163 | |||
| 164 | getFormExpose: async () => { | ||
| 165 | await getForm() | ||
| 166 | return unref(formRef) | ||
| 167 | }, | ||
| 168 | /** | ||
| 169 | * 根据schema拿到表单的完整数据(如果字段为空则返回空字符串) | ||
| 170 | * @param resultForm | ||
| 171 | * @param schemaParam | ||
| 172 | * @returns | ||
| 173 | */ | ||
| 174 | getFormResult: (resultForm, schemaParam: FormSchema[]) => { | ||
| 175 | let res = {} | ||
| 176 | schemaParam.forEach(item => { | ||
| 177 | res[item.field] = isEmptyVal(resultForm[item.field]) ? defaultEmptyValues(item.fieldType) : resultForm[item.field] | ||
| 178 | }) | ||
| 179 | return res | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | return { | ||
| 184 | formRegister: register, | ||
| 185 | formMethods: methods | ||
| 186 | } | ||
| 187 | } | ||
| 188 | |||
| 189 | /** | ||
| 190 | * | ||
| 191 | * @param reserveKeys 需要保留的key | ||
| 192 | * @param obj 需要删除的对象 | ||
| 193 | * @description 删除对象中的key | ||
| 194 | */ | ||
| 195 | export const reserveField = (reserveKeys: string[],obj) => { | ||
| 196 | let copy = cloneDeep(obj) | ||
| 197 | for (const key in copy) { | ||
| 198 | if (reserveKeys.indexOf(key) === -1) { | ||
| 199 | delete copy[key] | ||
| 200 | } | ||
| 201 | } | ||
| 202 | return copy | ||
| 203 | } |
| 1 | import dayjs from '@/utils/dayjs' | ||
| 2 | import useUserStore from '@/store/modules/user' | ||
| 3 | 1 | ||
| 4 | import { getOrganisationRelTreeListPromise, getTemplateListPromise } from "@/api/modules/dataBasic" | 2 | import { getOrganisationRelTreeListPromise, getTemplateListPromise } from "@/api/modules/dataBasic" |
| 5 | 3 | ||
| 6 | const currentDate = dayjs(new Date()).format('YYYY-MM-DD') | ||
| 7 | |||
| 8 | const isArray = (val: any): val is Array<any> => { | 4 | const isArray = (val: any): val is Array<any> => { |
| 9 | return val && Array.isArray(val) | 5 | return val && Array.isArray(val) |
| 10 | } | 6 | } |
| ... | @@ -15,81 +11,8 @@ const isNonEmptyArray = (val: any): boolean => { | ... | @@ -15,81 +11,8 @@ const isNonEmptyArray = (val: any): boolean => { |
| 15 | 11 | ||
| 16 | const useGetData = (param = {}) => { | 12 | const useGetData = (param = {}) => { |
| 17 | const BasicInfo: any = ref({}) // 基础资料 | 13 | const BasicInfo: any = ref({}) // 基础资料 |
| 18 | const gradeList = ref<any>([]) // 职级关系 | ||
| 19 | const platformGradeList = ref<any>([]) // 平台职级 | ||
| 20 | const postionList = ref<any>([]) // 职位 | ||
| 21 | const tenantRelList = ref<any>([]) // 公司关系 | ||
| 22 | const templateList = ref<any>([]) // 菜单模板列表 | 14 | const templateList = ref<any>([]) // 菜单模板列表 |
| 23 | const orgMap = ref<orgMapRes>() // 人员组织信息 | ||
| 24 | const organisationTree = ref<any>([]) // 组织树 | 15 | const organisationTree = ref<any>([]) // 组织树 |
| 25 | const personelTree = ref<any>([]) // 人员树 | ||
| 26 | const amoebaTree = ref<any>([]) // 阿米巴树 | ||
| 27 | const financeSubjectDict = ref<any>([]) // 关联财务科目字典 | ||
| 28 | const systemSideList = ref<{ // 系统列表 | ||
| 29 | systemName: string, | ||
| 30 | guid: string | ||
| 31 | }[]>([]) | ||
| 32 | const getFinanceSubject = async (customParam = {}) => { // 获取业务线 | ||
| 33 | return await budgetApi.getFinanceSubjectTreePromise((Object.assign({}, param, customParam))) | ||
| 34 | } | ||
| 35 | // !基础数据缓存机制 | ||
| 36 | async function getFinanceSubjectTree({ useCache = true, customParam = {} } = {}) { | ||
| 37 | if (isNonEmptyArray(BasicInfo.financeSubject) && useCache) { | ||
| 38 | return BasicInfo.financeSubject | ||
| 39 | } else { | ||
| 40 | let res = await getFinanceSubject(customParam) | ||
| 41 | BasicInfo.financeSubject = useCache ? res : [] | ||
| 42 | return res | ||
| 43 | } | ||
| 44 | } | ||
| 45 | async function getGradeTitleRelList({ useCache = true, customParam = {} } = {}) { // 获取职级关系列表 | ||
| 46 | if ( isNonEmptyArray(BasicInfo.gradeList) && useCache) { | ||
| 47 | gradeList.value = BasicInfo.gradeList | ||
| 48 | return gradeList.value | ||
| 49 | } else { | ||
| 50 | let res = await userApi.getGradeRelListPromise(customParam) | ||
| 51 | BasicInfo.gradeList = useCache ? res : [] | ||
| 52 | gradeList.value = res | ||
| 53 | return gradeList.value | ||
| 54 | } | ||
| 55 | } | ||
| 56 | |||
| 57 | async function getGradeList({ useCache = true, customParam = {} } = {}) { // 获取平台职级列表 | ||
| 58 | if ( isNonEmptyArray(BasicInfo.platformGradeList) && useCache) { | ||
| 59 | platformGradeList.value = BasicInfo.platformGradeList | ||
| 60 | return platformGradeList.value | ||
| 61 | } else { | ||
| 62 | let res = await userApi.getGradeListPromise(customParam) | ||
| 63 | BasicInfo.platformGradeList = useCache ? res : [] | ||
| 64 | platformGradeList.value = res | ||
| 65 | return platformGradeList.value | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | async function getPostionList({ useCache = true, customParam = {} } = {}) { // 获取职位 | ||
| 70 | if ( isNonEmptyArray(BasicInfo.postionList) && useCache) { | ||
| 71 | postionList.value = BasicInfo.postionList | ||
| 72 | return postionList.value | ||
| 73 | } else { | ||
| 74 | let res = await userApi.getPositionPromise(customParam) | ||
| 75 | BasicInfo.postionList = useCache ? res : [] | ||
| 76 | postionList.value = res | ||
| 77 | return postionList.value | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | async function getTenantRelList(useCache = true) { // 获取公司关系列表 | ||
| 82 | if (isNonEmptyArray(BasicInfo.tenantRelList) && useCache) { | ||
| 83 | tenantRelList.value = BasicInfo.tenantRelList | ||
| 84 | return tenantRelList.value | ||
| 85 | } else { | ||
| 86 | let res = await tenantApi.getTenantListPromise('Y') | ||
| 87 | BasicInfo.tenantRelList = useCache ? res : [] | ||
| 88 | tenantRelList.value = res | ||
| 89 | return tenantRelList.value | ||
| 90 | } | ||
| 91 | } | ||
| 92 | |||
| 93 | async function getPermissionTemplateList({ useCache = true, customParam = {} } = {}) { // 获取菜单模板 | 16 | async function getPermissionTemplateList({ useCache = true, customParam = {} } = {}) { // 获取菜单模板 |
| 94 | if (isNonEmptyArray(templateList.value) && useCache) { | 17 | if (isNonEmptyArray(templateList.value) && useCache) { |
| 95 | return templateList.value | 18 | return templateList.value |
| ... | @@ -100,47 +23,6 @@ const useGetData = (param = {}) => { | ... | @@ -100,47 +23,6 @@ const useGetData = (param = {}) => { |
| 100 | } | 23 | } |
| 101 | } | 24 | } |
| 102 | 25 | ||
| 103 | interface orgMapRes { | ||
| 104 | [key:string]:{ | ||
| 105 | guid:string, // staffGuid | ||
| 106 | userGuid:string, // userGuid | ||
| 107 | orgGuid:string, // 部门 | ||
| 108 | orgName:string, | ||
| 109 | orgGuidTop:string, // 一级部门guid | ||
| 110 | orgNameTop:number, | ||
| 111 | } | ||
| 112 | } | ||
| 113 | // 获取人员组织信息 | ||
| 114 | async function getOrgMap({ useCache = true, customParam = {} } = {}) { // 获取人员组织信息 | ||
| 115 | const userStore = useUserStore() | ||
| 116 | let staffGuid = userStore.userInfo.staffGuid | ||
| 117 | if (isObject(BasicInfo.orgMap) && BasicInfo.orgMap[staffGuid] && useCache) { | ||
| 118 | orgMap.value = BasicInfo.orgMap | ||
| 119 | return orgMap.value | ||
| 120 | } else { | ||
| 121 | const userStore = useUserStore() | ||
| 122 | let res = await staffApi.getOrgMap([userStore.userInfo.staffGuid]) | ||
| 123 | BasicInfo.orgMap = useCache ? res : null | ||
| 124 | orgMap.value = res | ||
| 125 | return orgMap.value | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | // 获取组织树 | ||
| 130 | async function getPersonelTree({ useCache = true, customParam = {} } = {}) { | ||
| 131 | if ( isNonEmptyArray(BasicInfo.personelTree) && useCache) { | ||
| 132 | personelTree.value = BasicInfo.personelTree | ||
| 133 | return personelTree.value | ||
| 134 | } else { | ||
| 135 | const userStore = useUserStore() | ||
| 136 | let res = await tenantApi.getOrganisationTreePromise(userStore.userInfo.tenantGuid) | ||
| 137 | mulTreeData(res) | ||
| 138 | BasicInfo.personelTree = useCache ? res : [] | ||
| 139 | personelTree.value = res | ||
| 140 | return personelTree.value | ||
| 141 | } | ||
| 142 | } | ||
| 143 | |||
| 144 | async function getOrganisationTree({ useCache = true, tenantGuid = '' } = {}) { // 获取组织树 | 26 | async function getOrganisationTree({ useCache = true, tenantGuid = '' } = {}) { // 获取组织树 |
| 145 | if ( isNonEmptyArray(BasicInfo.organisationTree) && useCache) { | 27 | if ( isNonEmptyArray(BasicInfo.organisationTree) && useCache) { |
| 146 | organisationTree.value = BasicInfo.organisationTree | 28 | organisationTree.value = BasicInfo.organisationTree |
| ... | @@ -154,59 +36,9 @@ const useGetData = (param = {}) => { | ... | @@ -154,59 +36,9 @@ const useGetData = (param = {}) => { |
| 154 | } | 36 | } |
| 155 | } | 37 | } |
| 156 | 38 | ||
| 157 | async function getAmoebaTree({ useCache = true, customParam = {} } = {}) { // 获取组织树 | ||
| 158 | if ( isNonEmptyArray(BasicInfo.amoebaTree) && useCache) { | ||
| 159 | amoebaTree.value = BasicInfo.amoebaTree | ||
| 160 | return amoebaTree.value | ||
| 161 | } else { | ||
| 162 | let res = await tenantApi.getAmoebaTreePromise2(currentDate) | ||
| 163 | getOrgtreeData(res,false) | ||
| 164 | BasicInfo.amoebaTree = useCache ? res : [] | ||
| 165 | amoebaTree.value = res | ||
| 166 | return amoebaTree.value | ||
| 167 | } | ||
| 168 | } | ||
| 169 | |||
| 170 | async function getFinanceDict({ useCache = true, customParam = {} } = {}) { // 获取财务科目关联字典 | ||
| 171 | if ( isNonEmptyArray(BasicInfo.financeSubjectDict) && useCache) { | ||
| 172 | financeSubjectDict.value = BasicInfo.financeSubjectDict | ||
| 173 | return financeSubjectDict.value | ||
| 174 | } else { | ||
| 175 | let res = await budgetApi.getSubjectDict(customParam) | ||
| 176 | getOrgtreeData(res,false) | ||
| 177 | BasicInfo.financeSubjectDict = useCache ? res : [] | ||
| 178 | financeSubjectDict.value = res | ||
| 179 | return financeSubjectDict.value | ||
| 180 | } | ||
| 181 | } | ||
| 182 | |||
| 183 | // 获取子系统列表 | ||
| 184 | async function getSystemSideList({ useCache = true, customParam = {} } = {}) { | ||
| 185 | if ( isNonEmptyArray(BasicInfo.systemSideList) && useCache) { | ||
| 186 | systemSideList.value = BasicInfo.systemSideList | ||
| 187 | return systemSideList.value | ||
| 188 | } else { | ||
| 189 | let res = await authApi.getSystemSideData() | ||
| 190 | BasicInfo.systemSideList = useCache ? res : [] | ||
| 191 | systemSideList.value = res | ||
| 192 | return systemSideList.value | ||
| 193 | } | ||
| 194 | } | ||
| 195 | |||
| 196 | return { | 39 | return { |
| 197 | getPersonelTree, | ||
| 198 | getFinanceSubject, | ||
| 199 | getGradeTitleRelList, | ||
| 200 | getGradeList, | ||
| 201 | getPostionList, | ||
| 202 | getTenantRelList, | ||
| 203 | getFinanceSubjectTree, | ||
| 204 | getPermissionTemplateList, | 40 | getPermissionTemplateList, |
| 205 | getOrgMap, | ||
| 206 | getOrganisationTree, | 41 | getOrganisationTree, |
| 207 | getAmoebaTree, | ||
| 208 | getFinanceDict, | ||
| 209 | getSystemSideList | ||
| 210 | } | 42 | } |
| 211 | } | 43 | } |
| 212 | 44 | ... | ... |
| ... | @@ -11,7 +11,6 @@ import useKeepAliveStore from '@/store/modules/keepAlive' | ... | @@ -11,7 +11,6 @@ import useKeepAliveStore from '@/store/modules/keepAlive' |
| 11 | import useUserStore from '@/store/modules/user' | 11 | import useUserStore from '@/store/modules/user' |
| 12 | import useMenuStore from '@/store/modules/menu' | 12 | import useMenuStore from '@/store/modules/menu' |
| 13 | import useRouteStore from '@/store/modules/route' | 13 | import useRouteStore from '@/store/modules/route' |
| 14 | import route from '@/mock/route' | ||
| 15 | 14 | ||
| 16 | const { isLoading } = useNProgress() | 15 | const { isLoading } = useNProgress() |
| 17 | 16 | ||
| ... | @@ -125,19 +124,19 @@ router.beforeEach(async (to, from, next) => { | ... | @@ -125,19 +124,19 @@ router.beforeEach(async (to, from, next) => { |
| 125 | } | 124 | } |
| 126 | } | 125 | } |
| 127 | else { | 126 | else { |
| 128 | if (to.name === 'home' || to.name == 'contactInfo' || to.name == 'register' || to.name == 'registerMobile' || to.name == 'homeDamRegister' || to.name == 'homeDamRegisterMobile' || to.name == 'homeDamFinance' || to.name == 'homeDamDataCircule' || | 127 | if (to.name == 'userPrivate' || to.name == 'userAgree' || to.name === 'home' || to.name == 'contactInfo' || to.name == 'register' || to.name == 'registerMobile' || to.name == 'homeDamRegister' || to.name == 'homeDamRegisterMobile' || to.name == 'homeDamFinance' || to.name == 'homeDamDataCircule' || |
| 129 | to.name == 'homeDamDemand' || to.name == 'homeDamAlgorithm' || to.name == 'homeDamAlgorithmMobile' || to.name == 'homeDamMarket' || to.name == 'homeDamMarketMobile' | 128 | to.name == 'homeDamDemand' || to.name == 'homeDamAlgorithm' || to.name == 'homeDamAlgorithmMobile' || to.name == 'homeDamMarket' || to.name == 'homeDamMarketMobile' |
| 130 | || to.name == 'homeDamDataCirculeMobile' || to.name == 'homeDamDemandMobile' || to.name == 'homeDamFinanceMobile') { | 129 | || to.name == 'homeDamDataCirculeMobile' || to.name == 'homeDamDemandMobile' || to.name == 'homeDamFinanceMobile') { |
| 131 | next() | 130 | next() |
| 132 | } | 131 | } |
| 133 | else if (!to.query.code && to.name !== 'login') { | 132 | else if (!to.query.code && to.name !== 'login') { |
| 134 | window.location.href = import.meta.env.VITE_IDASS_BASEURL; | 133 | // window.location.href = import.meta.env.VITE_IDASS_BASEURL; |
| 135 | // next({ | 134 | next({ |
| 136 | // name: 'login', | 135 | name: 'login', |
| 137 | // query: { | 136 | query: { |
| 138 | // redirect: to.fullPath !== '/' ? to.fullPath : undefined, | 137 | redirect: to.fullPath !== '/' ? to.fullPath : undefined, |
| 139 | // }, | 138 | }, |
| 140 | // }) | 139 | }) |
| 141 | } | 140 | } |
| 142 | else { | 141 | else { |
| 143 | next() | 142 | next() | ... | ... |
| ... | @@ -26,14 +26,30 @@ interface metaInfoRaw { | ... | @@ -26,14 +26,30 @@ interface metaInfoRaw { |
| 26 | 26 | ||
| 27 | // 固定路由(默认路由) | 27 | // 固定路由(默认路由) |
| 28 | const constantRoutes: RouteRecordRaw[] = [ | 28 | const constantRoutes: RouteRecordRaw[] = [ |
| 29 | // { | 29 | { |
| 30 | // path: '/login', | 30 | path: '/portalLogin', |
| 31 | // name: 'login', | 31 | name: 'login', |
| 32 | // component: () => import('@/views/login.vue'), | 32 | component: () => import('@/views/portal/portalLogin.vue'), |
| 33 | // meta: { | 33 | meta: { |
| 34 | // title: '登录', | 34 | title: '登录', |
| 35 | // }, | 35 | }, |
| 36 | // }, | 36 | }, |
| 37 | { | ||
| 38 | path: '/userPrivate', | ||
| 39 | name: 'userPrivate', | ||
| 40 | component: () => import('@/views/portal/userPrivate.vue'), | ||
| 41 | meta: { | ||
| 42 | title: '隐私声明', | ||
| 43 | }, | ||
| 44 | }, | ||
| 45 | { | ||
| 46 | path: '/userAgree', | ||
| 47 | name: 'userAgree', | ||
| 48 | component: () => import('@/views/portal/userAgree.vue'), | ||
| 49 | meta: { | ||
| 50 | title: '用户协议', | ||
| 51 | }, | ||
| 52 | }, | ||
| 37 | { | 53 | { |
| 38 | path: '/:all(.*)*', | 54 | path: '/:all(.*)*', |
| 39 | name: 'notFound', | 55 | name: 'notFound', | ... | ... |
src/store/modules/idaas.ts
0 → 100644
| 1 | |||
| 2 | import { idaasLogin } from '@/api/modules/idaas'; | ||
| 3 | import { autoSalt } from '@/utils/common'; | ||
| 4 | import { ElMessage } from 'element-plus'; | ||
| 5 | |||
| 6 | |||
| 7 | |||
| 8 | const useIdaas = defineStore( | ||
| 9 | // 唯一ID | ||
| 10 | 'idaas', | ||
| 11 | () => { | ||
| 12 | const idaasToken = ref('') // idaas token | ||
| 13 | const isLoginOut = ref(false);// idaas 退出登录。 | ||
| 14 | const idaasUserInfo = ref<{ | ||
| 15 | principal?:{ | ||
| 16 | logonUser:string, | ||
| 17 | mobileNo:string, | ||
| 18 | name:string | ||
| 19 | } | ||
| 20 | }>({}) | ||
| 21 | |||
| 22 | // 登录 | ||
| 23 | function login(data: any) { | ||
| 24 | data.username = data.logonUser; | ||
| 25 | data.password = autoSalt(data.password, false, false); | ||
| 26 | delete data.userType; | ||
| 27 | delete data.platformGuid; | ||
| 28 | delete data.logonUser; | ||
| 29 | data.needToastErr = 0; | ||
| 30 | data.telAreaCode = '+86'; | ||
| 31 | isLoginOut.value = false; | ||
| 32 | return idaasLogin(data).then((res: any) => { | ||
| 33 | if (res?.code == '00000') { | ||
| 34 | // ElMessage.success('登录成功'); | ||
| 35 | idaasUserInfo.value = res.data.data | ||
| 36 | return res.data; | ||
| 37 | } else { | ||
| 38 | ElMessage.error(res.msg) | ||
| 39 | } | ||
| 40 | }) | ||
| 41 | } | ||
| 42 | return { | ||
| 43 | idaasToken, | ||
| 44 | idaasUserInfo, | ||
| 45 | login | ||
| 46 | } | ||
| 47 | }, | ||
| 48 | { | ||
| 49 | persist:{ | ||
| 50 | storage: localStorage, | ||
| 51 | paths: ['idaasToken','idaasUserInfo'] | ||
| 52 | } | ||
| 53 | } | ||
| 54 | ) | ||
| 55 | |||
| 56 | export default useIdaas |
src/store/modules/login.ts
0 → 100644
| 1 | |||
| 2 | |||
| 3 | import { defineStore } from 'pinia' | ||
| 4 | |||
| 5 | const useLogin = defineStore( | ||
| 6 | // 唯一ID | ||
| 7 | 'login', | ||
| 8 | () => { | ||
| 9 | const isCheckSms = ref(false) | ||
| 10 | const smsValidateCode = ref(''); | ||
| 11 | const firstUnmetRequirement = ref(''); | ||
| 12 | const encodePwd = ref('') | ||
| 13 | return { | ||
| 14 | /** | ||
| 15 | * 密文 | ||
| 16 | */ | ||
| 17 | encodePwd, | ||
| 18 | /** | ||
| 19 | * 是否直接去校验验证码 | ||
| 20 | */ | ||
| 21 | isCheckSms, | ||
| 22 | /** | ||
| 23 | * 网关登录验证码 | ||
| 24 | */ | ||
| 25 | smsValidateCode, | ||
| 26 | /** | ||
| 27 | * 注册密码未满足的第一条规则的 label | ||
| 28 | */ | ||
| 29 | firstUnmetRequirement, | ||
| 30 | } | ||
| 31 | }, | ||
| 32 | { | ||
| 33 | persist:{ | ||
| 34 | storage: sessionStorage, | ||
| 35 | paths: ['firstUnmetRequirement','smsValidateCode','encodePwd'] | ||
| 36 | } | ||
| 37 | } | ||
| 38 | ) | ||
| 39 | |||
| 40 | export default useLogin | ||
| ... | \ No newline at end of file | ... | \ No newline at end of file |
src/store/modules/sysConfig.ts
0 → 100644
| 1 | const sysConfigStore = defineStore( | ||
| 2 | // 唯一ID | ||
| 3 | 'config', | ||
| 4 | () => { | ||
| 5 | let configMap: any = {}; | ||
| 6 | |||
| 7 | // 封装请求配置文件的函数 | ||
| 8 | const loadConfig = async () => { | ||
| 9 | try { | ||
| 10 | const response = await fetch('/config.json'); | ||
| 11 | if (!response.ok) { | ||
| 12 | throw new Error(`请求配置失败,状态码: ${response.status}`); | ||
| 13 | } | ||
| 14 | const config = await response.json(); | ||
| 15 | return config; | ||
| 16 | } catch (error) { | ||
| 17 | console.error('加载配置时出错:', error); | ||
| 18 | throw error; | ||
| 19 | } | ||
| 20 | }; | ||
| 21 | const setConfig = (val) => { | ||
| 22 | configMap = val | ||
| 23 | } | ||
| 24 | |||
| 25 | const getConfig = (field) => { | ||
| 26 | if (import.meta.env.MODE == 'nginx' || import.meta.env.MODE == 'development') { | ||
| 27 | return import.meta.env.VITE_appKey | ||
| 28 | } | ||
| 29 | return field ? configMap[field] : configMap; | ||
| 30 | } | ||
| 31 | |||
| 32 | return { | ||
| 33 | configMap, | ||
| 34 | loadConfig, | ||
| 35 | setConfig, | ||
| 36 | getConfig | ||
| 37 | } | ||
| 38 | }, | ||
| 39 | ) | ||
| 40 | |||
| 41 | export default sysConfigStore |
| ... | @@ -21,6 +21,7 @@ declare module '@vue/runtime-core' { | ... | @@ -21,6 +21,7 @@ declare module '@vue/runtime-core' { |
| 21 | Dialog_form: typeof import('./../components/Dialog/dialog_form.vue')['default'] | 21 | Dialog_form: typeof import('./../components/Dialog/dialog_form.vue')['default'] |
| 22 | Dialog_grid: typeof import('./../components/Dialog/dialog_grid.vue')['default'] | 22 | Dialog_grid: typeof import('./../components/Dialog/dialog_grid.vue')['default'] |
| 23 | Dialog_pane: typeof import('./../components/Dialog/dialog_pane.vue')['default'] | 23 | Dialog_pane: typeof import('./../components/Dialog/dialog_pane.vue')['default'] |
| 24 | DialogPlus: typeof import('./../components/DialogPlus/src/DialogPlus.vue')['default'] | ||
| 24 | Drawer: typeof import('./../components/Drawer/index.vue')['default'] | 25 | Drawer: typeof import('./../components/Drawer/index.vue')['default'] |
| 25 | EchartsMap: typeof import('./../components/EchartsMap/index.vue')['default'] | 26 | EchartsMap: typeof import('./../components/EchartsMap/index.vue')['default'] |
| 26 | Editor: typeof import('./../components/Editor/src/Editor.vue')['default'] | 27 | Editor: typeof import('./../components/Editor/src/Editor.vue')['default'] |
| ... | @@ -28,13 +29,17 @@ declare module '@vue/runtime-core' { | ... | @@ -28,13 +29,17 @@ declare module '@vue/runtime-core' { |
| 28 | FileUpload: typeof import('./../components/FileUpload/index.vue')['default'] | 29 | FileUpload: typeof import('./../components/FileUpload/index.vue')['default'] |
| 29 | FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default'] | 30 | FixedActionBar: typeof import('./../components/FixedActionBar/index.vue')['default'] |
| 30 | Form: typeof import('./../components/Form/index.vue')['default'] | 31 | Form: typeof import('./../components/Form/index.vue')['default'] |
| 32 | FormItem: typeof import('./../components/FormItem/FormItem.vue')['default'] | ||
| 33 | FormPlus: typeof import('./../components/FormPlus/src/FormPlus.vue')['default'] | ||
| 31 | GraphTopbar: typeof import('./../components/RelationNetwork/graphTopbar.vue')['default'] | 34 | GraphTopbar: typeof import('./../components/RelationNetwork/graphTopbar.vue')['default'] |
| 35 | Header: typeof import('./../components/Header/index.vue')['default'] | ||
| 32 | Hour: typeof import('./../components/Schedule/component/hour.vue')['default'] | 36 | Hour: typeof import('./../components/Schedule/component/hour.vue')['default'] |
| 33 | ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default'] | 37 | ImagePreview: typeof import('./../components/ImagePreview/index.vue')['default'] |
| 34 | ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default'] | 38 | ImagesUpload: typeof import('./../components/ImagesUpload/index.vue')['default'] |
| 35 | ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default'] | 39 | ImageUpload: typeof import('./../components/ImageUpload/index.vue')['default'] |
| 36 | LineageGraph: typeof import('./../components/LineageGraph/index.vue')['default'] | 40 | LineageGraph: typeof import('./../components/LineageGraph/index.vue')['default'] |
| 37 | ListPanel: typeof import('./../components/ListPanel/index.vue')['default'] | 41 | ListPanel: typeof import('./../components/ListPanel/index.vue')['default'] |
| 42 | Logo: typeof import('./../components/Logo/index.vue')['default'] | ||
| 38 | LookBpmn: typeof import('./../components/ApprovalProcess/src/components/LookBpmn.vue')['default'] | 43 | LookBpmn: typeof import('./../components/ApprovalProcess/src/components/LookBpmn.vue')['default'] |
| 39 | Month: typeof import('./../components/Schedule/component/month.vue')['default'] | 44 | Month: typeof import('./../components/Schedule/component/month.vue')['default'] |
| 40 | NotAllowed: typeof import('./../components/NotAllowed/index.vue')['default'] | 45 | NotAllowed: typeof import('./../components/NotAllowed/index.vue')['default'] |
| ... | @@ -46,6 +51,7 @@ declare module '@vue/runtime-core' { | ... | @@ -46,6 +51,7 @@ declare module '@vue/runtime-core' { |
| 46 | PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default'] | 51 | PcasCascader: typeof import('./../components/PcasCascader/index.vue')['default'] |
| 47 | Popover: typeof import('./../components/Popover/index.vue')['default'] | 52 | Popover: typeof import('./../components/Popover/index.vue')['default'] |
| 48 | RelationNetwork: typeof import('./../components/RelationNetwork/index.vue')['default'] | 53 | RelationNetwork: typeof import('./../components/RelationNetwork/index.vue')['default'] |
| 54 | Retrievepassword: typeof import('./../components/Retrievepassword/index.vue')['default'] | ||
| 49 | RouterLink: typeof import('vue-router')['RouterLink'] | 55 | RouterLink: typeof import('vue-router')['RouterLink'] |
| 50 | RouterView: typeof import('vue-router')['RouterView'] | 56 | RouterView: typeof import('vue-router')['RouterView'] |
| 51 | Schedule: typeof import('./../components/Schedule/index.vue')['default'] | 57 | Schedule: typeof import('./../components/Schedule/index.vue')['default'] | ... | ... |
| ... | @@ -7,6 +7,11 @@ type RecursivePartial<T> = { | ... | @@ -7,6 +7,11 @@ type RecursivePartial<T> = { |
| 7 | [P in keyof T]?: RecursivePartial<T[P]> | 7 | [P in keyof T]?: RecursivePartial<T[P]> |
| 8 | } | 8 | } |
| 9 | 9 | ||
| 10 | declare type ComponentRef<T> = InstanceType<T> | ||
| 11 | |||
| 12 | |||
| 13 | declare type Recordable<T = any, K = string> = Record<K extends null | undefined ? string : K, T> | ||
| 14 | |||
| 10 | declare namespace Settings { | 15 | declare namespace Settings { |
| 11 | interface app { | 16 | interface app { |
| 12 | /** | 17 | /** | ... | ... |
| ... | @@ -1255,3 +1255,71 @@ export const isAllPropertiesEmpty = (obj,keyList?: string[]) => { | ... | @@ -1255,3 +1255,71 @@ export const isAllPropertiesEmpty = (obj,keyList?: string[]) => { |
| 1255 | // 如果所有属性都有值,则返回true | 1255 | // 如果所有属性都有值,则返回true |
| 1256 | return false; | 1256 | return false; |
| 1257 | } | 1257 | } |
| 1258 | |||
| 1259 | /** 生成state的hash值 */ | ||
| 1260 | export const createStateHashCode = () => { | ||
| 1261 | const array = new Uint32Array(1); | ||
| 1262 | window.crypto.getRandomValues(array); | ||
| 1263 | return array[0].toString(16) + Date.now(); | ||
| 1264 | } | ||
| 1265 | |||
| 1266 | /** | ||
| 1267 | * 获取本地图 | ||
| 1268 | * @param name // 文件名 如 doc.png | ||
| 1269 | * @returns {*|string} | ||
| 1270 | */ | ||
| 1271 | export const getAssetsImages = (name) => { | ||
| 1272 | return new URL(`../assets/images/${name}`, import.meta.url).href; | ||
| 1273 | } | ||
| 1274 | |||
| 1275 | export const blobToImageLink = (data):Promise<string> => { | ||
| 1276 | return new Promise((resolve, reject) => { | ||
| 1277 | let blob = new Blob([data],{type:'image/png'}); // #识别文件类型 | ||
| 1278 | let objectUrl = URL.createObjectURL(blob); | ||
| 1279 | resolve(objectUrl) | ||
| 1280 | }) | ||
| 1281 | } | ||
| 1282 | |||
| 1283 | export function isJsonString(str) { | ||
| 1284 | if (typeof str !== 'string') return false; | ||
| 1285 | // 忽略前导和尾随空白字符 | ||
| 1286 | str = str.trim(); | ||
| 1287 | // 检查字符串是否以 { 或 [ 开始,并以 } 或 ] 结束 | ||
| 1288 | if (!str.startsWith('{') && !str.startsWith('[')) return false; | ||
| 1289 | if (!str.endsWith('}') && !str.endsWith(']')) return false; | ||
| 1290 | |||
| 1291 | try { | ||
| 1292 | JSON.parse(str); | ||
| 1293 | return true; | ||
| 1294 | } catch (error) { | ||
| 1295 | return false; | ||
| 1296 | } | ||
| 1297 | } | ||
| 1298 | |||
| 1299 | const toString = Object.prototype.toString | ||
| 1300 | |||
| 1301 | export const is = (val: unknown, type: string) => { | ||
| 1302 | return toString.call(val) === `[object ${type}]` | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | export const isEmptyVal = (val: any): boolean => { | ||
| 1306 | return val === '' || val === null || val === undefined | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | export const isObject = (val: any): val is Record<any, any> => { | ||
| 1310 | return val !== null && is(val, 'Object') | ||
| 1311 | } | ||
| 1312 | |||
| 1313 | /** | ||
| 1314 | * 首字母大写 | ||
| 1315 | */ | ||
| 1316 | export function firstUpperCase(str: string) { | ||
| 1317 | return str.toLowerCase().replace(/( |^)[a-z]/g, (L) => L.toUpperCase()) | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | /** | ||
| 1321 | * 驼峰转横杠 | ||
| 1322 | */ | ||
| 1323 | export const humpToDash = (str: string): string => { | ||
| 1324 | return str.replace(/([A-Z])/g, '-$1').toLowerCase() | ||
| 1325 | } | ... | ... |
src/utils/propTypes.ts
0 → 100644
| 1 | import { VueTypeValidableDef, VueTypesInterface, createTypes, toValidableType } from 'vue-types' | ||
| 2 | import { CSSProperties } from 'vue' | ||
| 3 | |||
| 4 | type PropTypes = VueTypesInterface & { | ||
| 5 | readonly style: VueTypeValidableDef<CSSProperties> | ||
| 6 | } | ||
| 7 | const newPropTypes = createTypes({ | ||
| 8 | func: undefined, | ||
| 9 | bool: undefined, | ||
| 10 | string: undefined, | ||
| 11 | number: undefined, | ||
| 12 | object: undefined, | ||
| 13 | integer: undefined | ||
| 14 | }) as PropTypes | ||
| 15 | |||
| 16 | class propTypes extends newPropTypes { | ||
| 17 | static get style() { | ||
| 18 | return toValidableType('style', { | ||
| 19 | type: [String, Object] | ||
| 20 | }) | ||
| 21 | } | ||
| 22 | } | ||
| 23 | |||
| 24 | export { propTypes } |
| 1 | import axios, {CancelTokenSource} from "axios"; | 1 | import axios, {CancelTokenSource} from "axios"; |
| 2 | import Storage from './composables/storage-helper'; | ||
| 3 | import CryptoHelper from './composables/cryptoJs-helper'; | 2 | import CryptoHelper from './composables/cryptoJs-helper'; |
| 4 | import { ElMessage } from 'element-plus' | 3 | import { ElMessage } from 'element-plus' |
| 5 | import useUserStore from '@/store/modules/user' | 4 | import useUserStore from '@/store/modules/user' |
| ... | @@ -13,9 +12,10 @@ interface Request { | ... | @@ -13,9 +12,10 @@ interface Request { |
| 13 | source: CancelTokenSource; | 12 | source: CancelTokenSource; |
| 14 | } | 13 | } |
| 15 | const pendingRequests: Request[] = []; | 14 | const pendingRequests: Request[] = []; |
| 16 | const storage = new Storage(); | ||
| 17 | const cryptoHelper = new CryptoHelper('cacheKey'); | 15 | const cryptoHelper = new CryptoHelper('cacheKey'); |
| 18 | 16 | ||
| 17 | const IDaaSBaseURL = 'idaas' | ||
| 18 | |||
| 19 | const service = axios.create({ | 19 | const service = axios.create({ |
| 20 | baseURL: (import.meta.env.VITE_OPEN_PROXY === 'true') ? `/api/` : `${import.meta.env.VITE_API_BASEURL}`, | 20 | baseURL: (import.meta.env.VITE_OPEN_PROXY === 'true') ? `/api/` : `${import.meta.env.VITE_API_BASEURL}`, |
| 21 | timeout: 10 * 60 * 1000,// request timeout | 21 | timeout: 10 * 60 * 1000,// request timeout |
| ... | @@ -52,9 +52,15 @@ service.interceptors.request.use( | ... | @@ -52,9 +52,15 @@ service.interceptors.request.use( |
| 52 | config.headers['real-ip'] = localStorage.getItem('ipAddress'); | 52 | config.headers['real-ip'] = localStorage.getItem('ipAddress'); |
| 53 | return config; | 53 | return config; |
| 54 | } | 54 | } |
| 55 | if (config.method === "idaasPost" || config.method === "idaaspost") { | ||
| 56 | config.baseURL = IDaaSBaseURL; | ||
| 57 | config.method = "post"; | ||
| 58 | } else if (config.method === "idaasGet" || config.method === "idaasget") { | ||
| 59 | config.baseURL = IDaaSBaseURL; | ||
| 60 | config.method = "get"; | ||
| 61 | } | ||
| 55 | if (config.responseType == "blob") { | 62 | if (config.responseType == "blob") { |
| 56 | // 文件流,文件名称相同时会判定同一个请求。 | 63 | // 文件流,文件名称相同时会判定同一个请求。 |
| 57 | const userStore = useUserStore(); | ||
| 58 | config.headers.Authorization = localStorage.getItem('token'); | 64 | config.headers.Authorization = localStorage.getItem('token'); |
| 59 | config.headers['real-ip'] = localStorage.getItem('ipAddress'); | 65 | config.headers['real-ip'] = localStorage.getItem('ipAddress'); |
| 60 | return config; | 66 | return config; | ... | ... |
src/views/portal/components/Header/index.vue
0 → 100644
| 1 | <script lang="ts" setup name="Header"> | ||
| 2 | import Logo from '../Logo/index.vue'; | ||
| 3 | import useSettingsStore from '@/store/modules/settings'; | ||
| 4 | |||
| 5 | |||
| 6 | const { proxy } = getCurrentInstance() as any; | ||
| 7 | |||
| 8 | const settingsStore = useSettingsStore(); | ||
| 9 | |||
| 10 | |||
| 11 | |||
| 12 | const currentPath = computed(() => proxy.$route.path) | ||
| 13 | |||
| 14 | |||
| 15 | |||
| 16 | onMounted(()=>{ | ||
| 17 | }) | ||
| 18 | </script> | ||
| 19 | |||
| 20 | <template> | ||
| 21 | <transition name="header"> | ||
| 22 | <header v-if="settingsStore.mode === 'pc' && settingsStore.settings.menu.menuMode === 'head'"> | ||
| 23 | <div class="header-container"> | ||
| 24 | <Logo /> | ||
| 25 | <!-- <Tools v-if="currentPath !== '/portalLogin'" /> --> | ||
| 26 | </div> | ||
| 27 | </header> | ||
| 28 | </transition> | ||
| 29 | </template> | ||
| 30 | |||
| 31 | <style lang="scss" scoped> | ||
| 32 | header { | ||
| 33 | // position: fixed; | ||
| 34 | // z-index: 1000; | ||
| 35 | top: 0; | ||
| 36 | left: 0; | ||
| 37 | right: 0; | ||
| 38 | display: flex; | ||
| 39 | align-items: center; | ||
| 40 | height: 64px; | ||
| 41 | color: var(--g-header-color); | ||
| 42 | // background: url('@/assets/images/header_bg.png') center/100% 100% no-repeat; | ||
| 43 | background-color: #fff; | ||
| 44 | transition: background-color 0.3s, var(--el-transition-color); | ||
| 45 | background-size: cover; | ||
| 46 | // position: relative; | ||
| 47 | |||
| 48 | .header-container { | ||
| 49 | width: var(--g-header-width); | ||
| 50 | height: 100%; | ||
| 51 | margin: 0 auto; | ||
| 52 | display: flex; | ||
| 53 | align-items: center; | ||
| 54 | justify-content: space-between; | ||
| 55 | position: relative; | ||
| 56 | } | ||
| 57 | |||
| 58 | .sidebar-collapse { | ||
| 59 | position: absolute; | ||
| 60 | left: calc(var(--g-sub-sidebar-width-portal) + 10px); | ||
| 61 | top: 50%; | ||
| 62 | transform: translateY(-50%); | ||
| 63 | z-index: 2; | ||
| 64 | cursor: pointer; | ||
| 65 | |||
| 66 | :deep(.el-icon) { | ||
| 67 | color: #fff; | ||
| 68 | } | ||
| 69 | } | ||
| 70 | |||
| 71 | .main { | ||
| 72 | width: calc(var(--g-header-width) - var(--g-sub-sidebar-width-portal) - 48px); | ||
| 73 | height: 100%; | ||
| 74 | padding-right: 24px; | ||
| 75 | } | ||
| 76 | |||
| 77 | @media screen and (max-width: var(--g-header-width)) { | ||
| 78 | .header-container { | ||
| 79 | width: 100%; | ||
| 80 | } | ||
| 81 | } | ||
| 82 | |||
| 83 | :deep(.title) { | ||
| 84 | position: relative; | ||
| 85 | width: calc(var(--g-sub-sidebar-width-portal) + 18px); | ||
| 86 | height: inherit; | ||
| 87 | padding-left: 18px; | ||
| 88 | padding-right: 0; | ||
| 89 | background-color: inherit; | ||
| 90 | flex-shrink: 0; | ||
| 91 | |||
| 92 | span { | ||
| 93 | font-size: 24px; | ||
| 94 | letter-spacing: 1px; | ||
| 95 | color: var(--g-header-color); | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | .nav { | ||
| 100 | display: flex; | ||
| 101 | width: 100%; | ||
| 102 | height: 100%; | ||
| 103 | padding: 0 25px; | ||
| 104 | align-items: center; | ||
| 105 | overflow-x: auto; | ||
| 106 | mask-image: linear-gradient(to right, transparent, #000 20px, #000 calc(100% - 20px), transparent); | ||
| 107 | |||
| 108 | // firefox隐藏滚动条 | ||
| 109 | scrollbar-width: none; | ||
| 110 | |||
| 111 | // chrome隐藏滚动条 | ||
| 112 | &::-webkit-scrollbar { | ||
| 113 | display: none; | ||
| 114 | } | ||
| 115 | |||
| 116 | .item-container { | ||
| 117 | position: relative; | ||
| 118 | display: flex; | ||
| 119 | width: initial; | ||
| 120 | height: inherit; | ||
| 121 | |||
| 122 | .item { | ||
| 123 | padding: 0 12px; | ||
| 124 | display: flex; | ||
| 125 | align-items: center; | ||
| 126 | justify-content: center; | ||
| 127 | flex-direction: column; | ||
| 128 | min-width: 60px; | ||
| 129 | height: 100%; | ||
| 130 | cursor: pointer; | ||
| 131 | color: var(--g-header-menu-color); | ||
| 132 | background-color: transparent; | ||
| 133 | // transition: background-color 0.3s, var(--el-transition-color); | ||
| 134 | |||
| 135 | &:hover { | ||
| 136 | color: var(--g-header-menu-hover-color); | ||
| 137 | background-color: var(--g-header-menu-hover-bg); | ||
| 138 | } | ||
| 139 | |||
| 140 | .el-icon { | ||
| 141 | font-size: 24px; | ||
| 142 | vertical-align: middle; | ||
| 143 | } | ||
| 144 | |||
| 145 | span { | ||
| 146 | text-align: center; | ||
| 147 | vertical-align: middle; | ||
| 148 | word-break: break-all; | ||
| 149 | |||
| 150 | @include text-overflow(1, false); | ||
| 151 | } | ||
| 152 | } | ||
| 153 | |||
| 154 | &.active .item { | ||
| 155 | color: var(--g-header-menu-active-color); | ||
| 156 | background-color: var(--g-header-menu-active-bg); | ||
| 157 | } | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | :deep(.tools) { | ||
| 162 | .buttons .item .el-icon { | ||
| 163 | color: var(--g-header-color); | ||
| 164 | } | ||
| 165 | |||
| 166 | .user-container { | ||
| 167 | font-size: 16px; | ||
| 168 | color: var(--g-header-color); | ||
| 169 | outline: none !important; | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | // 头部动画 | ||
| 175 | .header-enter-active, | ||
| 176 | .header-leave-active { | ||
| 177 | transition: transform 0.3s; | ||
| 178 | } | ||
| 179 | |||
| 180 | .header-enter-from, | ||
| 181 | .header-leave-to { | ||
| 182 | transform: translateY(calc(var(--g-header-height-portal) * -1)); | ||
| 183 | } | ||
| 184 | </style> |
src/views/portal/components/Logo/index.vue
0 → 100644
| 1 | <script lang="ts" setup name="Logo"> | ||
| 2 | import imgLogo from '@/assets/images/数据资产运营平台logo.png' | ||
| 3 | import useSettingsStore from '@/store/modules/settings' | ||
| 4 | |||
| 5 | defineProps({ | ||
| 6 | showLogo: { | ||
| 7 | type: Boolean, | ||
| 8 | default: true, | ||
| 9 | }, | ||
| 10 | showTitle: { | ||
| 11 | type: Boolean, | ||
| 12 | default: true, | ||
| 13 | }, | ||
| 14 | }) | ||
| 15 | |||
| 16 | const settingsStore = useSettingsStore() | ||
| 17 | |||
| 18 | const title = ref(import.meta.env.VITE_APP_TITLE) | ||
| 19 | const logo = ref(imgLogo) | ||
| 20 | |||
| 21 | const to = computed(() => { | ||
| 22 | const rtn: { | ||
| 23 | name?: string | ||
| 24 | } = {} | ||
| 25 | if (settingsStore.settings.home.enable) { | ||
| 26 | rtn.name = 'portal' | ||
| 27 | } | ||
| 28 | return rtn | ||
| 29 | }) | ||
| 30 | </script> | ||
| 31 | |||
| 32 | <template> | ||
| 33 | <div class="title"> | ||
| 34 | <img v-if="showLogo" :src="logo" class="logo"> | ||
| 35 | <!-- <router-link :to="to" :class="{ 'is-link': settingsStore.settings.home.enable }" :title="title"> | ||
| 36 | <img v-if="showLogo" :src="logo" class="logo"> | ||
| 37 | </router-link> | ||
| 38 | <el-divider direction="vertical" /> --> | ||
| 39 | </div> | ||
| 40 | |||
| 41 | </template> | ||
| 42 | |||
| 43 | <style lang="scss" scoped> | ||
| 44 | .title { | ||
| 45 | position: fixed; | ||
| 46 | z-index: 1000; | ||
| 47 | top: 0; | ||
| 48 | width: inherit; | ||
| 49 | padding: 0 10px; | ||
| 50 | display: flex; | ||
| 51 | align-items: center; | ||
| 52 | justify-content: space-between; | ||
| 53 | height: var(--g-sidebar-logo-height); | ||
| 54 | text-align: center; | ||
| 55 | overflow: hidden; | ||
| 56 | text-decoration: none; | ||
| 57 | |||
| 58 | &.is-link { | ||
| 59 | cursor: pointer; | ||
| 60 | } | ||
| 61 | |||
| 62 | .logo { | ||
| 63 | width: 264px; | ||
| 64 | height: 64px; | ||
| 65 | object-fit: contain; | ||
| 66 | |||
| 67 | &+span { | ||
| 68 | margin-left: 10px; | ||
| 69 | } | ||
| 70 | } | ||
| 71 | |||
| 72 | span { | ||
| 73 | display: block; | ||
| 74 | font-weight: bold; | ||
| 75 | color: #fff; | ||
| 76 | |||
| 77 | @include text-overflow; | ||
| 78 | } | ||
| 79 | |||
| 80 | .el-divider { | ||
| 81 | height: 1.5rem; | ||
| 82 | margin-right: 0; | ||
| 83 | } | ||
| 84 | |||
| 85 | } | ||
| 86 | </style> |
src/views/portal/components/Tools/index.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/portal/portalLogin.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/portal/userAgree.vue
0 → 100644
This diff is collapsed.
Click to expand it.
src/views/portal/userPrivate.vue
0 → 100644
This diff is collapsed.
Click to expand it.
| ... | @@ -35,11 +35,11 @@ export default ({ mode, command }) => { | ... | @@ -35,11 +35,11 @@ export default ({ mode, command }) => { |
| 35 | changeOrigin: env.VITE_OPEN_PROXY === 'true', | 35 | changeOrigin: env.VITE_OPEN_PROXY === 'true', |
| 36 | rewrite: path => path.replace(/\/api/, ''), | 36 | rewrite: path => path.replace(/\/api/, ''), |
| 37 | }, | 37 | }, |
| 38 | '/portal':{ | 38 | // '/portal':{ |
| 39 | target: env.VITE_API_PORTALURL, | 39 | // target: env.VITE_API_PORTALURL, |
| 40 | changeOrigin: env.VITE_OPEN_PROXY === 'true', | 40 | // changeOrigin: env.VITE_OPEN_PROXY === 'true', |
| 41 | rewrite: path => path.replace(/\/portal/, ''), | 41 | // rewrite: path => path.replace(/\/portal/, ''), |
| 42 | }, | 42 | // }, |
| 43 | '/circulation':{ | 43 | '/circulation':{ |
| 44 | target: env.VITE_APP_CIRCULATION, | 44 | target: env.VITE_APP_CIRCULATION, |
| 45 | changeOrigin: env.VITE_OPEN_PROXY === 'true', | 45 | changeOrigin: env.VITE_OPEN_PROXY === 'true', | ... | ... |
-
Please register or sign in to post a comment