dc1a7e5e by lihua

门户首页

1 parent 7731293f
Showing 45 changed files with 2092 additions and 195 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']
......
1 {
2 "appKey": "67bd3018e4b0cac8f9a5beeb"
3 }
...@@ -216,4 +216,4 @@ export const addorUpdateStaff = (param) => request({ ...@@ -216,4 +216,4 @@ export const addorUpdateStaff = (param) => request({
216 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/staff/save-or-update`, 216 url: `${import.meta.env.VITE_APP_PERSONAL_URL}/staff/save-or-update`,
217 method: 'put', 217 method: 'put',
218 data: param 218 data: param
219 }); 219 });
...\ No newline at end of file ...\ No newline at end of file
......
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
1 import DialogPlus from './src/DialogPlus.vue'
2
3 export { DialogPlus }
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>
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>
1 import FormItem from './FormItem.vue';
2 export default FormItem
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 }
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 }
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
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
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
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
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',
......
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
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
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 /**
......
...@@ -1254,4 +1254,72 @@ export const isAllPropertiesEmpty = (obj,keyList?: string[]) => { ...@@ -1254,4 +1254,72 @@ export const isAllPropertiesEmpty = (obj,keyList?: string[]) => {
1254 } 1254 }
1255 // 如果所有属性都有值,则返回true 1255 // 如果所有属性都有值,则返回true
1256 return false; 1256 return false;
1257 }
...\ No newline at end of file ...\ No newline at end of file
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 }
......
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;
......
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>
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>
...@@ -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',
......
Styling with Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!