Showing
42 changed files
with
620 additions
and
354 deletions
src/api/alarm/position/index.ts
0 → 100644
src/assets/images/bg-dark.png
0 → 100644
141 KB
src/assets/images/bg.png
0 → 100644
571 KB
... | ... | @@ -2,12 +2,18 @@ |
2 | 2 | * @Author: Vben |
3 | 3 | * @Description: logo component |
4 | 4 | --> |
5 | + | |
5 | 6 | <template> |
6 | 7 | <div class="anticon" :class="getAppLogoClass" @click="goHome"> |
7 | - <img src="../../../assets/images/logo.png" /> | |
8 | - <div class="ml-2 md:opacity-100" :class="getTitleClass" v-show="showTitle"> | |
9 | - {{ title }} | |
10 | - </div> | |
8 | + <img :src="getLogo" /> | |
9 | + <span | |
10 | + class="ml-2 md:opacity-100" | |
11 | + :class="getTitleClass" | |
12 | + v-show="showTitle" | |
13 | + style="white-space: nowrap" | |
14 | + > | |
15 | + {{ getTitle }} | |
16 | + </span> | |
11 | 17 | </div> |
12 | 18 | </template> |
13 | 19 | <script lang="ts" setup> |
... | ... | @@ -18,7 +24,6 @@ |
18 | 24 | import { useDesign } from '/@/hooks/web/useDesign'; |
19 | 25 | import { PageEnum } from '/@/enums/pageEnum'; |
20 | 26 | import { useUserStore } from '/@/store/modules/user'; |
21 | - | |
22 | 27 | const props = defineProps({ |
23 | 28 | /** |
24 | 29 | * The theme of the current parent component |
... | ... | @@ -45,7 +50,6 @@ |
45 | 50 | props.theme, |
46 | 51 | { 'collapsed-show-title': unref(getCollapsedShowTitle) }, |
47 | 52 | ]); |
48 | - | |
49 | 53 | const getTitleClass = computed(() => [ |
50 | 54 | `${prefixCls}__title`, |
51 | 55 | { |
... | ... | @@ -56,10 +60,22 @@ |
56 | 60 | function goHome() { |
57 | 61 | go(userStore.getUserInfo.homePath || PageEnum.BASE_HOME); |
58 | 62 | } |
63 | + const getLogo = computed(() => { | |
64 | + return userStore.platInfo?.logo ?? '/src/assets/images/logo.png'; | |
65 | + }); | |
66 | + const getTitle = computed(() => { | |
67 | + // 设置icon | |
68 | + let link = (document.querySelector("link[rel*='icon']") || | |
69 | + document.createElement('link')) as HTMLLinkElement; | |
70 | + link.type = 'image/x-icon'; | |
71 | + link.rel = 'shortcut icon'; | |
72 | + link.href = userStore.platInfo?.icon ?? '/public/favicon.ico'; | |
73 | + document.getElementsByTagName('head')[0].appendChild(link); | |
74 | + return userStore.platInfo?.name ?? title; | |
75 | + }); | |
59 | 76 | </script> |
60 | 77 | <style lang="less" scoped> |
61 | 78 | @prefix-cls: ~'@{namespace}-app-logo'; |
62 | - | |
63 | 79 | .@{prefix-cls} { |
64 | 80 | display: flex; |
65 | 81 | align-items: center; | ... | ... |
... | ... | @@ -29,6 +29,7 @@ export const APP_LOCAL_CACHE_KEY = 'COMMON__LOCAL__KEY__'; |
29 | 29 | // base global session key |
30 | 30 | export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__'; |
31 | 31 | |
32 | +export const PLATFORM = 'PLATFORM'; | |
32 | 33 | export enum CacheTypeEnum { |
33 | 34 | SESSION, |
34 | 35 | LOCAL, | ... | ... |
1 | 1 | export enum RoleEnum { |
2 | - ROLE_SYS_ADMIN = 'ROLE_SYS_ADMIN', | |
3 | - ROLE_TENANT_ADMIN = 'ROLE_TENANT_ADMIN', | |
4 | - ROLE_NORMAL_USER = 'ROLE_NORMAL_USER', | |
5 | - ROLE_PLATFORM_ADMIN = 'ROLE_PLATFORM_ADMIN', | |
2 | + ROLE_SYS_ADMIN = 'SYS_ADMIN', | |
3 | + ROLE_TENANT_ADMIN = 'TENANT_ADMIN', | |
4 | + ROLE_NORMAL_USER = 'CUSTOMER_USER', | |
5 | + ROLE_PLATFORM_ADMIN = 'PLATFORM_ADMIN', | |
6 | 6 | } | ... | ... |
... | ... | @@ -25,8 +25,10 @@ import { router } from '/@/router'; |
25 | 25 | import { usePermissionStore } from '/@/store/modules/permission'; |
26 | 26 | import { RouteRecordRaw } from 'vue-router'; |
27 | 27 | import { PAGE_NOT_FOUND_ROUTE } from '/@/router/routes/basic'; |
28 | - | |
28 | +import { createLocalStorage } from '/@/utils/cache/index'; | |
29 | 29 | interface UserState { |
30 | + platInfo: any; | |
31 | + enterPriseInfo: any; | |
30 | 32 | userInfo: Nullable<UserInfo>; |
31 | 33 | token?: string; |
32 | 34 | roleList: RoleEnum[]; |
... | ... | @@ -36,9 +38,13 @@ interface UserState { |
36 | 38 | refreshToken?: string; |
37 | 39 | } |
38 | 40 | |
41 | +const storage = createLocalStorage(); | |
39 | 42 | export const useUserStore = defineStore({ |
40 | 43 | id: 'app-user', |
41 | 44 | state: (): UserState => ({ |
45 | + //平台信息 | |
46 | + platInfo: storage.get('platInfo') || null, | |
47 | + enterPriseInfo: storage.get('enterPriseInfo') || null, | |
42 | 48 | // user info |
43 | 49 | userInfo: null, |
44 | 50 | // token |
... | ... | @@ -52,7 +58,11 @@ export const useUserStore = defineStore({ |
52 | 58 | // Last fetch time |
53 | 59 | lastUpdateTime: 0, |
54 | 60 | }), |
61 | + | |
55 | 62 | getters: { |
63 | + getPlatInfo(): any { | |
64 | + return this.platInfo; | |
65 | + }, | |
56 | 66 | getUserInfo(): UserInfo { |
57 | 67 | return this.userInfo || getAuthCache<UserInfo>(USER_INFO_KEY) || {}; |
58 | 68 | }, |
... | ... | @@ -73,6 +83,12 @@ export const useUserStore = defineStore({ |
73 | 83 | }, |
74 | 84 | }, |
75 | 85 | actions: { |
86 | + setPlatInfo(platInfo: any) { | |
87 | + this.platInfo = platInfo; | |
88 | + }, | |
89 | + setEnterPriseInfo(enterPriseInfo: any) { | |
90 | + this.enterPriseInfo = enterPriseInfo; | |
91 | + }, | |
76 | 92 | storeToken(jwtToken: string, refreshToken: string) { |
77 | 93 | this.jwtToken = jwtToken; |
78 | 94 | this.refreshToken = refreshToken; | ... | ... |
... | ... | @@ -5,6 +5,7 @@ import type { RouteLocationNormalized } from 'vue-router'; |
5 | 5 | import { createLocalStorage, createSessionStorage } from '/@/utils/cache'; |
6 | 6 | import { Memory } from './memory'; |
7 | 7 | import { |
8 | + PLATFORM, | |
8 | 9 | TOKEN_KEY, |
9 | 10 | JWT_TOKEN_KEY, |
10 | 11 | REFRESH_TOKEN_KEY, |
... | ... | @@ -21,6 +22,7 @@ import { toRaw } from 'vue'; |
21 | 22 | import { pick, omit } from 'lodash-es'; |
22 | 23 | |
23 | 24 | interface BasicStore { |
25 | + [PLATFORM]: Object; | |
24 | 26 | [TOKEN_KEY]: string | number | null | undefined; |
25 | 27 | [JWT_TOKEN_KEY]: string | number | null | undefined; |
26 | 28 | [REFRESH_TOKEN_KEY]: string | number | null | undefined; | ... | ... |
1 | -import { BasicColumn } from '/@/components/Table'; | |
2 | - | |
3 | -import { FormSchema } from '/@/components/Table'; | |
1 | +import type { BasicColumn } from '/@/components/Table'; | |
2 | +import type { FormSchema } from '/@/components/Table'; | |
3 | +import { getOrganizationList } from '/@/api/system/system'; | |
4 | +import { copyTransFun } from '/@/utils/fnUtils'; | |
5 | +import { getDeviceProfile } from '/@/api/alarm/position'; | |
4 | 6 | export const formSchema: FormSchema[] = [ |
5 | 7 | { |
6 | - field: 'organization', | |
8 | + field: 'organizationId', | |
7 | 9 | label: '', |
8 | - component: 'TreeSelect', | |
10 | + component: 'ApiTreeSelect', | |
9 | 11 | componentProps: { |
10 | - placeholder: '请选择组织', | |
12 | + api: async () => { | |
13 | + const data = await getOrganizationList(); | |
14 | + copyTransFun(data as any as any[]); | |
15 | + return data; | |
16 | + }, | |
11 | 17 | }, |
12 | 18 | }, |
13 | 19 | { |
14 | - field: 'organization', | |
20 | + field: 'profileId', | |
15 | 21 | label: '', |
16 | - component: 'Select', | |
22 | + component: 'ApiSelect', | |
17 | 23 | componentProps: { |
24 | + api: getDeviceProfile, | |
18 | 25 | placeholder: '请选择设备配置', |
26 | + labelField: 'name', | |
27 | + valueField: 'id', | |
19 | 28 | }, |
20 | 29 | }, |
21 | 30 | { |
22 | - field: 'device', | |
31 | + field: 'name', | |
23 | 32 | label: '', |
24 | 33 | component: 'Input', |
25 | 34 | componentProps: { |
... | ... | @@ -27,19 +36,28 @@ export const formSchema: FormSchema[] = [ |
27 | 36 | }, |
28 | 37 | }, |
29 | 38 | { |
30 | - field: 'status', | |
39 | + field: 'deviceState', | |
31 | 40 | label: '', |
32 | 41 | component: 'RadioGroup', |
33 | 42 | componentProps: { |
34 | 43 | size: 'small', |
35 | 44 | options: [ |
36 | - { label: '全部', value: 'Apple' }, | |
37 | - { label: '离线', value: 'Pear' }, | |
38 | - { label: '在线', value: 'Orange' }, | |
45 | + { label: '待激活', value: 'INACTIVE' }, | |
46 | + { label: '在线', value: 'ONLINE' }, | |
47 | + { label: '离线', value: 'OFFLINE' }, | |
39 | 48 | { label: '报警', value: 'hhh' }, |
40 | 49 | ], |
41 | 50 | }, |
42 | 51 | }, |
52 | + { | |
53 | + field: 'alarmStatus', | |
54 | + label: '', | |
55 | + component: 'RadioGroup', | |
56 | + componentProps: { | |
57 | + size: 'small', | |
58 | + options: [{ label: '是否报警', value: '' }], | |
59 | + }, | |
60 | + }, | |
43 | 61 | ]; |
44 | 62 | |
45 | 63 | export const columns: BasicColumn[] = [ |
... | ... | @@ -57,5 +75,6 @@ export const columns: BasicColumn[] = [ |
57 | 75 | title: '状态', |
58 | 76 | dataIndex: 'deviceState', |
59 | 77 | width: 100, |
78 | + slots: { customRender: 'deviceState' }, | |
60 | 79 | }, |
61 | 80 | ]; | ... | ... |
... | ... | @@ -2,7 +2,28 @@ |
2 | 2 | <div class="wrapper"> |
3 | 3 | <div ref="wrapRef" :style="{ height, width }"> </div> |
4 | 4 | <div class="right-wrap"> |
5 | - <BasicTable @register="registerTable" /> | |
5 | + <BasicTable @register="registerTable"> | |
6 | + <template #deviceState="{ record }"> | |
7 | + <Tag | |
8 | + :color=" | |
9 | + record.deviceState == DeviceState.INACTIVE | |
10 | + ? 'warning' | |
11 | + : record.deviceState == DeviceState.ONLINE | |
12 | + ? 'success' | |
13 | + : 'error' | |
14 | + " | |
15 | + class="ml-2" | |
16 | + > | |
17 | + {{ | |
18 | + record.deviceState == DeviceState.INACTIVE | |
19 | + ? '待激活' | |
20 | + : record.deviceState == DeviceState.ONLINE | |
21 | + ? '在线' | |
22 | + : '离线' | |
23 | + }} | |
24 | + </Tag> | |
25 | + </template></BasicTable | |
26 | + > | |
6 | 27 | </div> |
7 | 28 | </div> |
8 | 29 | </template> |
... | ... | @@ -12,11 +33,13 @@ |
12 | 33 | import { formSchema, columns } from './config.data'; |
13 | 34 | import { BasicTable, useTable } from '/@/components/Table'; |
14 | 35 | import { devicePage } from '/@/api/alarm/contact/alarmContact'; |
15 | - | |
36 | + import { Tag } from 'ant-design-vue'; | |
37 | + import { DeviceState } from '/@/api/device/model/deviceModel'; | |
16 | 38 | export default defineComponent({ |
17 | 39 | name: 'BaiduMap', |
18 | 40 | components: { |
19 | 41 | BasicTable, |
42 | + Tag, | |
20 | 43 | }, |
21 | 44 | props: { |
22 | 45 | width: { |
... | ... | @@ -55,8 +78,6 @@ |
55 | 78 | formConfig: { |
56 | 79 | labelWidth: 120, |
57 | 80 | schemas: formSchema, |
58 | - showAdvancedButton: false, | |
59 | - showActionButtonGroup: false, | |
60 | 81 | }, |
61 | 82 | showIndexColumn: false, |
62 | 83 | useSearchForm: true, |
... | ... | @@ -68,6 +89,7 @@ |
68 | 89 | wrapRef, |
69 | 90 | registerTable, |
70 | 91 | handleSuccess, |
92 | + DeviceState, | |
71 | 93 | }; |
72 | 94 | }, |
73 | 95 | }); |
... | ... | @@ -83,12 +105,10 @@ |
83 | 105 | .right-wrap { |
84 | 106 | padding-top: 10px; |
85 | 107 | width: 22%; |
86 | - height: 80%; | |
108 | + height: 95%; | |
87 | 109 | position: absolute; |
88 | 110 | right: 5%; |
89 | - top: 10%; | |
90 | - } | |
91 | - .scroll-wrap { | |
92 | - height: 450px; | |
111 | + top: 3%; | |
112 | + background-color: #fff; | |
93 | 113 | } |
94 | 114 | </style> | ... | ... |
src/views/dashboard/analysis/components/DynamicInfo.vue
renamed from
src/views/dashboard/workbench/components/DynamicInfo.vue
src/views/dashboard/analysis/components/ProjectCard.vue
renamed from
src/views/dashboard/workbench/components/ProjectCard.vue
src/views/dashboard/analysis/components/QuickNav.vue
renamed from
src/views/dashboard/workbench/components/QuickNav.vue
src/views/dashboard/analysis/components/SaleRadar.vue
renamed from
src/views/dashboard/workbench/components/SaleRadar.vue
src/views/dashboard/analysis/components/WorkbenchHeader.vue
renamed from
src/views/dashboard/workbench/components/WorkbenchHeader.vue
src/views/dashboard/analysis/components/data.ts
renamed from
src/views/dashboard/workbench/components/data.ts
1 | 1 | <template> |
2 | - <div class="p-4 md:flex"> | |
3 | - <div class="md:w-7/10 w-full !mr-4 enter-y"> | |
4 | - <GrowCard :loading="loading" class="enter-y" /> | |
5 | - <SiteAnalysis class="!my-4 enter-y" :loading="loading" /> | |
6 | - <div class="md:flex enter-y"> | |
7 | - <Card title="核心流程指南" style="width: 100%"> | |
8 | - <img alt="核心流程指南" src="../../../assets/images/flow.png" /> | |
2 | + <PageWrapper> | |
3 | + <template #headerContent> <WorkbenchHeader /> </template> | |
4 | + <div class="lg:flex"> | |
5 | + <div class="lg:w-7/10 w-full !mr-4 enter-y"> | |
6 | + <ProjectCard :loading="loading" class="enter-y" /> | |
7 | + <DynamicInfo :loading="loading" class="!my-4 enter-y" /> | |
8 | + </div> | |
9 | + <div class="lg:w-3/10 w-full enter-y"> | |
10 | + <QuickNav :loading="loading" class="enter-y" /> | |
11 | + | |
12 | + <Card class="!my-4 enter-y" :loading="loading"> | |
13 | + <img class="xl:h-50 h-30 mx-auto" src="../../../assets/svg/illustration.svg" /> | |
9 | 14 | </Card> |
15 | + | |
16 | + <SaleRadar :loading="loading" class="enter-y" /> | |
10 | 17 | </div> |
11 | 18 | </div> |
12 | - <div class="md:w-3/10 w-full enter-y"> | |
13 | - <HelpDoc /> | |
14 | - </div> | |
15 | - </div> | |
19 | + <BasicModal | |
20 | + @register="register" | |
21 | + v-bind="$attrs" | |
22 | + :mask="true" | |
23 | + :showCancelBtn="false" | |
24 | + :showOkBtn="false" | |
25 | + :canFullscreen="false" | |
26 | + :closable="false" | |
27 | + :maskStyle="maskColor" | |
28 | + :height="600" | |
29 | + :width="1500" | |
30 | + :maskClosable="false" | |
31 | + title="请修改密码" | |
32 | + :helpMessage="['请您修改密码']" | |
33 | + > | |
34 | + <PasswordDialog /> | |
35 | + </BasicModal> | |
36 | + </PageWrapper> | |
16 | 37 | </template> |
17 | 38 | <script lang="ts" setup> |
18 | - import { ref } from 'vue'; | |
19 | - import GrowCard from './components/GrowCard.vue'; | |
20 | - import SiteAnalysis from './components/SiteAnalysis.vue'; | |
39 | + import PasswordDialog from '/@/views/system/password/index.vue'; | |
40 | + import { BasicModal, useModal } from '/@/components/Modal'; | |
41 | + import { getAuthCache } from '/@/utils/auth'; | |
42 | + import { USER_INFO_KEY } from '/@/enums/cacheEnum'; | |
43 | + import { ref, onMounted } from 'vue'; | |
21 | 44 | import { Card } from 'ant-design-vue'; |
22 | - import HelpDoc from './components/HelpDoc.vue'; | |
23 | - const loading = ref(true); | |
45 | + import { PageWrapper } from '/@/components/Page'; | |
46 | + import WorkbenchHeader from './components/WorkbenchHeader.vue'; | |
47 | + import ProjectCard from './components/ProjectCard.vue'; | |
48 | + import QuickNav from './components/QuickNav.vue'; | |
49 | + import DynamicInfo from './components/DynamicInfo.vue'; | |
50 | + import SaleRadar from './components/SaleRadar.vue'; | |
51 | + | |
52 | + const maskColor = ref({ backgroundColor: 'grey' }); | |
53 | + const statusModel = ref(false); | |
54 | + const [register, { openModal }] = useModal(); | |
55 | + onMounted(() => { | |
56 | + const userInfo: object = getAuthCache(USER_INFO_KEY); | |
57 | + if (userInfo.needSetPwd == true) { | |
58 | + statusModel.value = true; | |
59 | + openModal(statusModel.value); | |
60 | + } else if (userInfo.needSetPwd == false) { | |
61 | + openModal(statusModel.value); | |
62 | + } | |
63 | + }); | |
64 | + //Yunteng123456!!! | |
65 | + //Password123456! | |
66 | + //System123456! | |
67 | + //Testrole123456! | |
68 | + const loading = ref(false); | |
24 | 69 | setTimeout(() => { |
25 | 70 | loading.value = false; |
26 | 71 | }, 1500); | ... | ... |
src/views/dashboard/workbench/components/GrowCard.vue
renamed from
src/views/dashboard/analysis/components/GrowCard.vue
src/views/dashboard/workbench/components/HelpDoc.vue
renamed from
src/views/dashboard/analysis/components/HelpDoc.vue
... | ... | @@ -34,13 +34,13 @@ |
34 | 34 | </Card> |
35 | 35 | <Card hoverable title="联系我们" :bordered="false"> |
36 | 36 | <template #cover> |
37 | - <QrCode :value="qrCodeUrl" class="flex justify-center" /> | |
37 | + <img :src="getQrCode" alt="" style="width: 150px; height: 150px; margin: 50px auto" /> | |
38 | 38 | </template> |
39 | 39 | <CardMeta> |
40 | 40 | <template #description> |
41 | - <p>联系人: 张三</p> | |
42 | - <p>联系电话: 15912341234</p> | |
43 | - <p>联系地址: 四川省成都市剑南大道北段中1533号 </p> | |
41 | + <p>联系人: {{ getContacts }}</p> | |
42 | + <p>联系电话: {{ getTel }}</p> | |
43 | + <p>联系地址: {{ getAddress }} </p> | |
44 | 44 | </template> |
45 | 45 | </CardMeta> |
46 | 46 | </Card> |
... | ... | @@ -48,9 +48,10 @@ |
48 | 48 | </template> |
49 | 49 | |
50 | 50 | <script lang="ts"> |
51 | - import { defineComponent, ref } from 'vue'; | |
51 | + import { defineComponent, ref, computed, onMounted } from 'vue'; | |
52 | 52 | import { Card, AnchorLink, List, ListItem, ListItemMeta, Avatar, CardMeta } from 'ant-design-vue'; |
53 | - import { QrCode } from '/@/components/Qrcode/index'; | |
53 | + import { useUserStore } from '/@/store/modules/user'; | |
54 | + import { getEnterPriseDetail } from '/@/api/oem'; | |
54 | 55 | export default defineComponent({ |
55 | 56 | components: { |
56 | 57 | Card, |
... | ... | @@ -60,9 +61,12 @@ |
60 | 61 | ListItemMeta, |
61 | 62 | Avatar, |
62 | 63 | CardMeta, |
63 | - QrCode, | |
64 | 64 | }, |
65 | 65 | setup() { |
66 | + onMounted(async () => { | |
67 | + const res = await getEnterPriseDetail(); | |
68 | + userStore.setEnterPriseInfo(res); | |
69 | + }); | |
66 | 70 | const helpDoc = ref([ |
67 | 71 | { |
68 | 72 | title: '如何接入设备?', |
... | ... | @@ -137,7 +141,19 @@ |
137 | 141 | }, |
138 | 142 | ]; |
139 | 143 | |
140 | - const qrCodeUrl = 'https://www.vvbin.cn'; | |
144 | + const userStore = useUserStore(); | |
145 | + const getContacts = computed(() => { | |
146 | + return userStore.enterPriseInfo?.contacts; | |
147 | + }); | |
148 | + const getAddress = computed(() => { | |
149 | + return userStore.enterPriseInfo?.address; | |
150 | + }); | |
151 | + const getTel = computed(() => { | |
152 | + return userStore.enterPriseInfo?.tel; | |
153 | + }); | |
154 | + const getQrCode = computed(() => { | |
155 | + return userStore.enterPriseInfo?.qrCode; | |
156 | + }); | |
141 | 157 | |
142 | 158 | return { |
143 | 159 | activeKey, |
... | ... | @@ -145,7 +161,10 @@ |
145 | 161 | onTabChange, |
146 | 162 | data, |
147 | 163 | helpDoc, |
148 | - qrCodeUrl, | |
164 | + getQrCode, | |
165 | + getContacts, | |
166 | + getAddress, | |
167 | + getTel, | |
149 | 168 | }; |
150 | 169 | }, |
151 | 170 | }); | ... | ... |
src/views/dashboard/workbench/components/SiteAnalysis.vue
renamed from
src/views/dashboard/analysis/components/SiteAnalysis.vue
src/views/dashboard/workbench/components/VisitAnalysis.vue
renamed from
src/views/dashboard/analysis/components/VisitAnalysis.vue
src/views/dashboard/workbench/components/VisitAnalysisBar.vue
renamed from
src/views/dashboard/analysis/components/VisitAnalysisBar.vue
src/views/dashboard/workbench/components/props.ts
renamed from
src/views/dashboard/analysis/components/props.ts
src/views/dashboard/workbench/data.ts
renamed from
src/views/dashboard/analysis/data.ts
1 | 1 | <template> |
2 | - <PageWrapper> | |
3 | - <template #headerContent> <WorkbenchHeader /> </template> | |
4 | - <div class="lg:flex"> | |
5 | - <div class="lg:w-7/10 w-full !mr-4 enter-y"> | |
6 | - <ProjectCard :loading="loading" class="enter-y" /> | |
7 | - <DynamicInfo :loading="loading" class="!my-4 enter-y" /> | |
8 | - </div> | |
9 | - <div class="lg:w-3/10 w-full enter-y"> | |
10 | - <QuickNav :loading="loading" class="enter-y" /> | |
11 | - | |
12 | - <Card class="!my-4 enter-y" :loading="loading"> | |
13 | - <img class="xl:h-50 h-30 mx-auto" src="../../../assets/svg/illustration.svg" /> | |
2 | + <div class="p-4 md:flex"> | |
3 | + <div class="md:w-7/10 w-full !mr-4 enter-y"> | |
4 | + <GrowCard :loading="loading" class="enter-y" /> | |
5 | + <SiteAnalysis class="!my-4 enter-y" :loading="loading" /> | |
6 | + <div class="md:flex enter-y"> | |
7 | + <Card title="核心流程指南" style="width: 100%"> | |
8 | + <img alt="核心流程指南" src="../../../assets/images/flow.png" /> | |
14 | 9 | </Card> |
15 | - | |
16 | - <SaleRadar :loading="loading" class="enter-y" /> | |
17 | 10 | </div> |
18 | 11 | </div> |
19 | - <BasicModal | |
20 | - @register="register" | |
21 | - v-bind="$attrs" | |
22 | - :mask="true" | |
23 | - :showCancelBtn="false" | |
24 | - :showOkBtn="false" | |
25 | - :canFullscreen="false" | |
26 | - :closable="false" | |
27 | - :maskStyle="maskColor" | |
28 | - :height="600" | |
29 | - :width="1500" | |
30 | - :maskClosable="false" | |
31 | - title="请修改密码" | |
32 | - :helpMessage="['请您修改密码']" | |
33 | - > | |
34 | - <PasswordDialog /> | |
35 | - </BasicModal> | |
36 | - </PageWrapper> | |
12 | + <div class="md:w-3/10 w-full enter-y"> | |
13 | + <HelpDoc /> | |
14 | + </div> | |
15 | + </div> | |
37 | 16 | </template> |
38 | 17 | <script lang="ts" setup> |
39 | - import PasswordDialog from '/@/views/system/password/index.vue'; | |
40 | - import { BasicModal, useModal } from '/@/components/Modal'; | |
41 | - import { getAuthCache } from '/@/utils/auth'; | |
42 | - import { USER_INFO_KEY } from '/@/enums/cacheEnum'; | |
43 | - import { ref, onMounted } from 'vue'; | |
18 | + import { ref } from 'vue'; | |
19 | + import GrowCard from './components/GrowCard.vue'; | |
20 | + import SiteAnalysis from './components/SiteAnalysis.vue'; | |
44 | 21 | import { Card } from 'ant-design-vue'; |
45 | - import { PageWrapper } from '/@/components/Page'; | |
46 | - import WorkbenchHeader from './components/WorkbenchHeader.vue'; | |
47 | - import ProjectCard from './components/ProjectCard.vue'; | |
48 | - import QuickNav from './components/QuickNav.vue'; | |
49 | - import DynamicInfo from './components/DynamicInfo.vue'; | |
50 | - import SaleRadar from './components/SaleRadar.vue'; | |
51 | - | |
52 | - const maskColor = ref({ backgroundColor: 'grey' }); | |
53 | - const statusModel = ref(false); | |
54 | - const [register, { openModal }] = useModal(); | |
55 | - onMounted(() => { | |
56 | - const userInfo: object = getAuthCache(USER_INFO_KEY); | |
57 | - if (userInfo.needSetPwd == true) { | |
58 | - statusModel.value = true; | |
59 | - openModal(statusModel.value); | |
60 | - } else if (userInfo.needSetPwd == false) { | |
61 | - openModal(statusModel.value); | |
62 | - } | |
63 | - }); | |
64 | - //Yunteng123456!!! | |
65 | - //Password123456! | |
66 | - //System123456! | |
67 | - //Testrole123456! | |
68 | - const loading = ref(false); | |
22 | + import HelpDoc from './components/HelpDoc.vue'; | |
23 | + const loading = ref(true); | |
69 | 24 | setTimeout(() => { |
70 | 25 | loading.value = false; |
71 | 26 | }, 1500); | ... | ... |
... | ... | @@ -51,7 +51,7 @@ |
51 | 51 | props: { |
52 | 52 | userData: { type: Object }, |
53 | 53 | }, |
54 | - emits: ['reload'], | |
54 | + emits: ['reload', 'register'], | |
55 | 55 | setup(_, { emit }) { |
56 | 56 | const DeviceStep1Ref = ref<InstanceType<typeof DeviceStep1>>(); |
57 | 57 | const DeviceStep2Ref = ref<InstanceType<typeof DeviceStep2>>(); | ... | ... |
1 | 1 | <template> |
2 | - <div :class="prefixCls" class="relative w-full h-full px-4"> | |
3 | - <AppLocalePicker | |
4 | - class="absolute text-white top-4 right-4 enter-x xl:text-gray-600" | |
5 | - :showText="false" | |
6 | - v-if="!sessionTimeout && showLocale" | |
7 | - /> | |
8 | - <AppDarkModeToggle class="absolute top-3 right-7 enter-x" v-if="!sessionTimeout" /> | |
9 | - | |
10 | - <span class="-enter-x xl:hidden"> | |
11 | - <AppLogo :alwaysShowTitle="true" /> | |
12 | - </span> | |
13 | - | |
14 | - <div class="container relative h-full py-2 mx-auto sm:px-10"> | |
15 | - <div class="flex h-full"> | |
16 | - <div class="hidden min-h-full pl-4 mr-4 xl:flex xl:flex-col xl:w-6/12"> | |
17 | - <AppLogo class="-enter-x" /> | |
18 | - <div class="my-auto"> | |
19 | - <img :alt="title" src="../../../assets/images/iot.png" class="w-4/5 -mt-16 -enter-x" /> | |
20 | - <div class="mt-10 font-medium text-white -enter-x"> | |
21 | - <span class="inline-block mt-4 text-3xl"> {{ t('sys.login.signInTitle') }}</span> | |
22 | - </div> | |
23 | - <div class="mt-5 font-normal text-white text-md dark:text-gray-500 -enter-x"> | |
24 | - {{ t('sys.login.signInDesc') }} | |
25 | - </div> | |
26 | - </div> | |
27 | - </div> | |
28 | - <div class="flex w-full h-full py-5 xl:h-auto xl:py-0 xl:my-0 xl:w-6/12"> | |
29 | - <div | |
30 | - :class="`${prefixCls}-form`" | |
31 | - class=" | |
32 | - relative | |
33 | - w-full | |
34 | - px-5 | |
35 | - py-8 | |
36 | - mx-auto | |
37 | - my-auto | |
38 | - rounded-md | |
39 | - shadow-md | |
40 | - xl:ml-16 xl:bg-transparent | |
41 | - sm:px-8 | |
42 | - xl:p-4 xl:shadow-none | |
43 | - sm:w-3/4 | |
44 | - lg:w-2/4 | |
45 | - xl:w-auto | |
46 | - enter-x | |
47 | - " | |
48 | - > | |
49 | - <LoginForm /> | |
50 | - <ForgetPasswordForm /> | |
51 | - <RegisterForm /> | |
52 | - <MobileForm /> | |
53 | - <QrCodeForm /> | |
54 | - </div> | |
2 | + <div class="login-page"> | |
3 | + <div class="login-header" :style="{ backgroundColor: isDark ? '#1d3794' : '#22283a' }"> | |
4 | + <AppLogo | |
5 | + :alwaysShowTitle="true" | |
6 | + style="z-index: 99; position: absolute; width: 40px; height: 100px; left: 10%; top: -20px" | |
7 | + /> | |
8 | + <AppLocalePicker | |
9 | + class="absolute text-white top-4 right-4 enter-x xl:text-gray-600" | |
10 | + :showText="false" | |
11 | + v-if="!sessionTimeout && showLocale" | |
12 | + /> | |
13 | + <AppDarkModeToggle | |
14 | + class="absolute top-3 right-7 enter-x" | |
15 | + v-if="!sessionTimeout" | |
16 | + @click="toggleDark" | |
17 | + /> | |
18 | + </div> | |
19 | + <div class="login-container" :class="{ light1: isDark, dark: !isDark }"> | |
20 | + <div class="login-description"> | |
21 | + <h1>物联网平台</h1> | |
22 | + <h2>输入您的个人详细信息开始使用!</h2> | |
23 | + </div> | |
24 | + <div class="flex w-full h-full py-5 xl:h-auto xl:py-0 xl:my-0 xl:w-6/12"> | |
25 | + <div | |
26 | + :class="`${prefixCls}-form`" | |
27 | + class=" | |
28 | + relative | |
29 | + w-full | |
30 | + px-5 | |
31 | + py-8 | |
32 | + mx-auto | |
33 | + my-auto | |
34 | + rounded-md | |
35 | + shadow-md | |
36 | + xl:ml-16 xl:bg-transparent | |
37 | + sm:px-8 | |
38 | + xl:p-4 xl:shadow-none | |
39 | + sm:w-3/4 | |
40 | + lg:w-2/4 | |
41 | + xl:w-auto | |
42 | + enter-x | |
43 | + " | |
44 | + :style="{ backgroundColor: isDark ? '#fff' : '#1a2030' }" | |
45 | + > | |
46 | + <LoginForm /> | |
47 | + <ForgetPasswordForm /> | |
48 | + <RegisterForm /> | |
49 | + <MobileForm /> | |
50 | + <QrCodeForm /> | |
55 | 51 | </div> |
56 | 52 | </div> |
53 | + <div style="position: absolute; bottom: 20px; left: 45%">{{ getCopyRight }}</div> | |
57 | 54 | </div> |
58 | 55 | </div> |
59 | 56 | </template> |
60 | 57 | <script lang="ts" setup> |
61 | - import { computed } from 'vue'; | |
58 | + import { computed, ref } from 'vue'; | |
62 | 59 | import { AppLogo } from '/@/components/Application'; |
63 | 60 | import { AppLocalePicker, AppDarkModeToggle } from '/@/components/Application'; |
64 | 61 | import LoginForm from './LoginForm.vue'; |
... | ... | @@ -70,6 +67,7 @@ |
70 | 67 | import { useI18n } from '/@/hooks/web/useI18n'; |
71 | 68 | import { useDesign } from '/@/hooks/web/useDesign'; |
72 | 69 | import { useLocaleStore } from '/@/store/modules/locale'; |
70 | + import { useUserStore } from '/@/store/modules/user'; | |
73 | 71 | |
74 | 72 | defineProps({ |
75 | 73 | sessionTimeout: { |
... | ... | @@ -83,145 +81,69 @@ |
83 | 81 | const localeStore = useLocaleStore(); |
84 | 82 | const showLocale = localeStore.getShowPicker; |
85 | 83 | const title = computed(() => globSetting?.title ?? ''); |
84 | + const isDark = ref(true); | |
85 | + const toggleDark = () => { | |
86 | + isDark.value = !isDark.value; | |
87 | + }; | |
88 | + const userStore = useUserStore(); | |
89 | + const getCopyRight = computed(() => { | |
90 | + return (userStore.platInfo?.copyright ?? '') + (userStore.platInfo?.presentedOurselves ?? ''); | |
91 | + }); | |
86 | 92 | </script> |
87 | 93 | <style lang="less"> |
88 | 94 | @prefix-cls: ~'@{namespace}-login'; |
89 | 95 | @logo-prefix-cls: ~'@{namespace}-app-logo'; |
90 | 96 | @countdown-prefix-cls: ~'@{namespace}-countdown-input'; |
91 | 97 | @dark-bg: #293146; |
92 | - | |
93 | - html[data-theme='dark'] { | |
94 | - .@{prefix-cls} { | |
95 | - background-color: @dark-bg; | |
96 | - | |
97 | - &::before { | |
98 | - background-image: url(/@/assets/svg/login-bg-dark.svg); | |
99 | - } | |
100 | - | |
101 | - .ant-input, | |
102 | - .ant-input-password { | |
103 | - background-color: #232a3b; | |
104 | - } | |
105 | - | |
106 | - .ant-btn:not(.ant-btn-link):not(.ant-btn-primary) { | |
107 | - border: 1px solid #4a5569; | |
108 | - } | |
109 | - | |
110 | - &-form { | |
111 | - background: transparent !important; | |
112 | - } | |
113 | - | |
114 | - .app-iconify { | |
115 | - color: #fff; | |
116 | - } | |
117 | - } | |
118 | - | |
119 | - input.fix-auto-fill, | |
120 | - .fix-auto-fill input { | |
121 | - -webkit-text-fill-color: #c9d1d9 !important; | |
122 | - box-shadow: inherit !important; | |
123 | - } | |
98 | + .light1 { | |
99 | + background-image: url('/src/assets/images/bg.png'); | |
124 | 100 | } |
125 | - | |
126 | - .@{prefix-cls} { | |
127 | - min-height: 100%; | |
128 | - overflow: hidden; | |
129 | - @media (max-width: @screen-xl) { | |
130 | - background-color: #293146; | |
131 | - | |
132 | - .@{prefix-cls}-form { | |
133 | - background-color: #fff; | |
134 | - } | |
135 | - } | |
136 | - | |
137 | - &::before { | |
138 | - position: absolute; | |
139 | - top: 0; | |
140 | - left: 0; | |
101 | + .dark { | |
102 | + background-image: url('/src/assets/images/bg-dark.png'); | |
103 | + } | |
104 | + .login-page { | |
105 | + width: 100%; | |
106 | + height: 100%; | |
107 | + position: relative; | |
108 | + .login-header { | |
109 | + height: 60px; | |
141 | 110 | width: 100%; |
142 | - height: 100%; | |
143 | - margin-left: -48%; | |
144 | - background-image: url(/@/assets/svg/login-bg.svg); | |
145 | - background-position: 100%; | |
146 | - background-repeat: no-repeat; | |
147 | - background-size: auto 100%; | |
148 | - content: ''; | |
149 | - @media (max-width: @screen-xl) { | |
150 | - display: none; | |
111 | + background-color: #1d3794; | |
112 | + .vben-dark-switch { | |
113 | + margin-left: 1840px; | |
151 | 114 | } |
152 | 115 | } |
153 | 116 | |
154 | - .@{logo-prefix-cls} { | |
155 | - position: absolute; | |
156 | - top: 12px; | |
157 | - height: 30px; | |
158 | - | |
159 | - &__title { | |
160 | - font-size: 16px; | |
161 | - color: #fff; | |
162 | - } | |
117 | + .login-container { | |
118 | + position: relative; | |
119 | + left: -6px; | |
120 | + right: 20px; | |
121 | + width: 101vw; | |
122 | + height: calc(100% - 60px); | |
123 | + background-size: 100% 100%; | |
124 | + background-repeat: no-repeat; | |
125 | + .vben-login-form { | |
126 | + position: absolute; | |
163 | 127 | |
164 | - img { | |
165 | - width: 32px; | |
128 | + width: 410px; | |
129 | + right: 200px; | |
130 | + top: 50%; | |
131 | + margin-top: -180px; | |
166 | 132 | } |
167 | - } | |
168 | - | |
169 | - .container { | |
170 | - .@{logo-prefix-cls} { | |
171 | - display: flex; | |
172 | - width: 60%; | |
173 | - height: 80px; | |
174 | - | |
175 | - &__title { | |
176 | - font-size: 24px; | |
177 | - color: #fff; | |
133 | + .login-description { | |
134 | + position: absolute; | |
135 | + right: 13%; | |
136 | + top: 15%; | |
137 | + h1 { | |
138 | + font-size: 26px; | |
139 | + color: #5aeeed; | |
140 | + text-align: center; | |
178 | 141 | } |
179 | - | |
180 | - img { | |
181 | - width: 48px; | |
142 | + h2 { | |
143 | + font-size: 20px; | |
144 | + color: #5aeeed; | |
182 | 145 | } |
183 | 146 | } |
184 | 147 | } |
185 | - | |
186 | - &-sign-in-way { | |
187 | - .anticon { | |
188 | - font-size: 22px; | |
189 | - color: #888; | |
190 | - cursor: pointer; | |
191 | - | |
192 | - &:hover { | |
193 | - color: @primary-color; | |
194 | - } | |
195 | - } | |
196 | - } | |
197 | - | |
198 | - input:not([type='checkbox']) { | |
199 | - min-width: 360px; | |
200 | - | |
201 | - @media (max-width: @screen-xl) { | |
202 | - min-width: 320px; | |
203 | - } | |
204 | - | |
205 | - @media (max-width: @screen-lg) { | |
206 | - min-width: 260px; | |
207 | - } | |
208 | - | |
209 | - @media (max-width: @screen-md) { | |
210 | - min-width: 240px; | |
211 | - } | |
212 | - | |
213 | - @media (max-width: @screen-sm) { | |
214 | - min-width: 160px; | |
215 | - } | |
216 | - } | |
217 | - | |
218 | - .@{countdown-prefix-cls} input { | |
219 | - min-width: unset; | |
220 | - } | |
221 | - | |
222 | - .ant-divider-inner-text { | |
223 | - font-size: 12px; | |
224 | - color: @text-color-secondary; | |
225 | - } | |
226 | 148 | } |
227 | 149 | </style> | ... | ... |
... | ... | @@ -44,7 +44,7 @@ |
44 | 44 | </ARow> |
45 | 45 | <ARow class="enter-x"> |
46 | 46 | <ACol :span="12"> |
47 | - <FormItem> </FormItem> | |
47 | + <FormItem /> | |
48 | 48 | </ACol> |
49 | 49 | <ACol :span="12"> |
50 | 50 | <FormItem :style="{ 'text-align': 'right' }"> |
... | ... | @@ -60,7 +60,7 @@ |
60 | 60 | <script lang="ts" setup> |
61 | 61 | import { reactive, ref, toRaw, unref, computed } from 'vue'; |
62 | 62 | |
63 | - import { Checkbox, Form, Input, Row, Col, Button, Divider } from 'ant-design-vue'; | |
63 | + import { Form, Input, Row, Col, Button } from 'ant-design-vue'; | |
64 | 64 | import LoginFormTitle from './LoginFormTitle.vue'; |
65 | 65 | |
66 | 66 | import { useI18n } from '/@/hooks/web/useI18n'; |
... | ... | @@ -69,7 +69,8 @@ |
69 | 69 | import { useUserStore } from '/@/store/modules/user'; |
70 | 70 | import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin'; |
71 | 71 | import { useDesign } from '/@/hooks/web/useDesign'; |
72 | - //import { onKeyStroke } from '@vueuse/core'; | |
72 | + import { getPlatForm } from '/@/api/oem/index'; | |
73 | + import { createLocalStorage } from '/@/utils/cache/index'; | |
73 | 74 | |
74 | 75 | const ACol = Col; |
75 | 76 | const ARow = Row; |
... | ... | @@ -78,14 +79,12 @@ |
78 | 79 | const { t } = useI18n(); |
79 | 80 | const { notification, createErrorModal } = useMessage(); |
80 | 81 | const { prefixCls } = useDesign('login'); |
81 | - const userStore = useUserStore(); | |
82 | 82 | |
83 | 83 | const { setLoginState, getLoginState } = useLoginState(); |
84 | 84 | const { getFormRules } = useFormRules(); |
85 | - | |
85 | + const userStore = useUserStore(); | |
86 | 86 | const formRef = ref(); |
87 | 87 | const loading = ref(false); |
88 | - const rememberMe = ref(false); | |
89 | 88 | |
90 | 89 | const formData = reactive({ |
91 | 90 | account: 'sysadmin', |
... | ... | @@ -116,8 +115,11 @@ |
116 | 115 | description: `${t('sys.login.loginSuccessDesc')}: ${userInfo.realName}`, |
117 | 116 | duration: 3, |
118 | 117 | }); |
118 | + const res = await getPlatForm(); | |
119 | + const storage = createLocalStorage(); | |
120 | + storage.set('platformInfo', res); | |
119 | 121 | } |
120 | - } catch (error) { | |
122 | + } catch (error: any) { | |
121 | 123 | createErrorModal({ |
122 | 124 | title: t('sys.api.loginFailed'), |
123 | 125 | content: |
... | ... | @@ -131,5 +133,15 @@ |
131 | 133 | } finally { |
132 | 134 | loading.value = false; |
133 | 135 | } |
136 | + | |
137 | + const res = await getPlatForm(); | |
138 | + userStore.setPlatInfo(res); | |
139 | + // 设置icon | |
140 | + let link = (document.querySelector("link[rel*='icon']") || | |
141 | + document.createElement('link')) as HTMLLinkElement; | |
142 | + link.type = 'image/x-icon'; | |
143 | + link.rel = 'shortcut icon'; | |
144 | + link.href = res.icon ?? '/public/favicon.ico'; | |
145 | + document.getElementsByTagName('head')[0].appendChild(link); | |
134 | 146 | } |
135 | 147 | </script> | ... | ... |
... | ... | @@ -4,7 +4,7 @@ |
4 | 4 | <div class="enter-x min-w-64 min-h-64"> |
5 | 5 | <QrCode |
6 | 6 | :value="qrCodeUrl" |
7 | - class="enter-x flex justify-center xl:justify-start" | |
7 | + class="enter-x flex justify-center xl:justify-center" | |
8 | 8 | :width="280" |
9 | 9 | /> |
10 | 10 | <Divider class="enter-x">{{ t('sys.login.scanSign') }}</Divider> | ... | ... |
... | ... | @@ -52,7 +52,7 @@ |
52 | 52 | import MenuDrawer from './MenuDrawer.vue'; |
53 | 53 | |
54 | 54 | // 导入列 属性,和搜索栏内容 |
55 | - import { columns, searchFormSchema } from './menu.data'; | |
55 | + import { columns } from './menu.data'; | |
56 | 56 | import { useI18n } from '/@/hooks/web/useI18n'; |
57 | 57 | import { notification } from 'ant-design-vue'; |
58 | 58 | |
... | ... | @@ -65,7 +65,6 @@ |
65 | 65 | const { t } = useI18n(); //加载国际化 |
66 | 66 | // 新增菜单 |
67 | 67 | const getI18nCreateMenu = computed(() => t('routes.common.system.pageSystemTitleCreateMenu')); |
68 | - console.log(111); | |
69 | 68 | const [registerTable, { reload, expandAll }] = useTable({ |
70 | 69 | title: t('routes.common.system.pageSystemTitleMenuList'), //'菜单列表' |
71 | 70 | api: getMenuList, //加载数据 | ... | ... |
... | ... | @@ -98,10 +98,11 @@ |
98 | 98 | const { createMessage } = useMessage(); |
99 | 99 | const [tenantAdminFormDrawer, { openDrawer: openTenantAdminFormDrawer }] = useDrawer(); |
100 | 100 | |
101 | + const tenantId = ref(''); | |
101 | 102 | function handleCreateTenantAdmin() { |
102 | 103 | openTenantAdminFormDrawer(true, { |
103 | 104 | isUpdate: false, |
104 | - tenantCode: tenantCode, | |
105 | + tenantId: tenantId.value, | |
105 | 106 | }); |
106 | 107 | } |
107 | 108 | |
... | ... | @@ -109,7 +110,7 @@ |
109 | 110 | openTenantAdminFormDrawer(true, { |
110 | 111 | isUpdate: true, |
111 | 112 | record, |
112 | - tenantCode: tenantCode, | |
113 | + tenantCode: tenantId.value, | |
113 | 114 | }); |
114 | 115 | } |
115 | 116 | |
... | ... | @@ -160,7 +161,6 @@ |
160 | 161 | slots: { customRender: 'status' }, |
161 | 162 | }, |
162 | 163 | ]; |
163 | - const tenantCode = ref(''); | |
164 | 164 | const [tenantAdminTable, { reload }] = useTable({ |
165 | 165 | api: getTenantAdminPage, |
166 | 166 | columns: tenantAdminColumns as BasicColumn[], |
... | ... | @@ -168,7 +168,7 @@ |
168 | 168 | bordered: true, |
169 | 169 | showIndexColumn: false, |
170 | 170 | searchInfo: { |
171 | - tenantCode: tenantCode, | |
171 | + tenantId: tenantId, | |
172 | 172 | roleType: RoleEnum.ROLE_TENANT_ADMIN, |
173 | 173 | }, |
174 | 174 | actionColumn: { |
... | ... | @@ -186,8 +186,8 @@ |
186 | 186 | }); |
187 | 187 | //默认传递页面数据 |
188 | 188 | const [tenantAdminDrawer, { closeDrawer }] = useDrawerInner(async (data) => { |
189 | - tenantCode.value = data.record.tenantCode; | |
190 | - await reload(); | |
189 | + tenantId.value = data.record.tenantId; | |
190 | + reload(); | |
191 | 191 | }); |
192 | 192 | |
193 | 193 | //提交按钮 |
... | ... | @@ -204,7 +204,7 @@ |
204 | 204 | handleCreateTenantAdmin, |
205 | 205 | handleSubmit, |
206 | 206 | tenantAdminTable, |
207 | - tenantCode, | |
207 | + tenantId, | |
208 | 208 | tenantAdminFormDrawer, |
209 | 209 | handleSuccess, |
210 | 210 | handleEdit, | ... | ... |
... | ... | @@ -7,7 +7,7 @@ |
7 | 7 | width="60%" |
8 | 8 | @ok="handleSubmit" |
9 | 9 | > |
10 | - <BasicForm @register="tenantAdminForm"> </BasicForm> | |
10 | + <BasicForm @register="tenantAdminForm" /> | |
11 | 11 | </BasicDrawer> |
12 | 12 | </template> |
13 | 13 | <script lang="ts"> |
... | ... | @@ -25,7 +25,7 @@ |
25 | 25 | }, |
26 | 26 | setup(_, { emit }) { |
27 | 27 | const isUpdate = ref(true); |
28 | - const tenantCode = ref(''); | |
28 | + const tenantId = ref(''); | |
29 | 29 | const formSchema: FormSchema[] = [ |
30 | 30 | { |
31 | 31 | field: 'id', |
... | ... | @@ -81,7 +81,7 @@ |
81 | 81 | async (data) => { |
82 | 82 | await resetFields(); |
83 | 83 | isUpdate.value = !!data?.isUpdate; |
84 | - tenantCode.value = data?.tenantCode; | |
84 | + tenantId.value = data?.tenantId; | |
85 | 85 | await updateSchema({ field: 'title', componentProps: { disabled: false } }); |
86 | 86 | if (unref(isUpdate)) { |
87 | 87 | await setFieldsValue({ |
... | ... | @@ -106,7 +106,7 @@ |
106 | 106 | typeof values.tenantExpireTime != 'undefined' && values.tenantExpireTime != null |
107 | 107 | ? values.tenantExpireTime.format('YYYY-MM-DD HH:mm:ss') |
108 | 108 | : null, |
109 | - tenantCode: tenantCode.value, | |
109 | + tenantId: tenantId.value, | |
110 | 110 | }; |
111 | 111 | setDrawerProps({ confirmLoading: true }); |
112 | 112 | saveTenantAdmin(requestParams as any as UserDTO).then(() => { | ... | ... |
... | ... | @@ -35,8 +35,8 @@ |
35 | 35 | :before-upload="beforeUploadIconPic" |
36 | 36 | > |
37 | 37 | <div v-if="iconPic"> |
38 | - <img :src="iconPic" /> | |
39 | - <div style="background-color: #ccc">重新上传</div> | |
38 | + <img :src="iconPic" class="m-auto" /> | |
39 | + <div style="background-color: #ccc; margin-top: 20px">重新上传</div> | |
40 | 40 | </div> |
41 | 41 | <div v-else> |
42 | 42 | <PlusOutlined style="font-size: 30px" /> |
... | ... | @@ -95,6 +95,8 @@ |
95 | 95 | import type { FileItem } from '/@/components/Upload/src/typing'; |
96 | 96 | import { logoUpload, iconUpload, bgUpload, getPlatForm, updatePlatForm } from '/@/api/oem/index'; |
97 | 97 | import { PlusOutlined } from '@ant-design/icons-vue'; |
98 | + import { useUserStore } from '/@/store/modules/user'; | |
99 | + import { createLocalStorage } from '/@/utils/cache/index'; | |
98 | 100 | export default defineComponent({ |
99 | 101 | components: { |
100 | 102 | BasicForm, |
... | ... | @@ -111,6 +113,8 @@ |
111 | 113 | tip: '拼命加载中...', |
112 | 114 | }); |
113 | 115 | const { createMessage } = useMessage(); |
116 | + const userStore = useUserStore(); | |
117 | + const storage = createLocalStorage(); | |
114 | 118 | const [registerForm, { getFieldsValue, setFieldsValue }] = useForm({ |
115 | 119 | schemas, |
116 | 120 | showSubmitButton: false, |
... | ... | @@ -146,7 +150,7 @@ |
146 | 150 | } |
147 | 151 | return isJpgOrPng && isLt2M; |
148 | 152 | }; |
149 | - | |
153 | + // Icon上传 | |
150 | 154 | async function customUploadIconPic({ file }) { |
151 | 155 | if (beforeUploadIconPic(file)) { |
152 | 156 | const formData = new FormData(); |
... | ... | @@ -158,9 +162,9 @@ |
158 | 162 | } |
159 | 163 | } |
160 | 164 | const beforeUploadIconPic = (file: FileItem) => { |
161 | - const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'; | |
165 | + const isJpgOrPng = file.type === 'image/x-icon'; | |
162 | 166 | if (!isJpgOrPng) { |
163 | - createMessage.error('只能上传图片文件!'); | |
167 | + createMessage.error('只能上传.icon图片文件!'); | |
164 | 168 | } |
165 | 169 | const isLt2M = (file.size as number) / 1024 < 500; |
166 | 170 | if (!isLt2M) { |
... | ... | @@ -196,18 +200,28 @@ |
196 | 200 | try { |
197 | 201 | const fieldValue = getFieldsValue(); |
198 | 202 | compState.value.loading = true; |
199 | - await updatePlatForm({ | |
203 | + const newFieldValue = { | |
200 | 204 | ...fieldValue, |
201 | - background: unref(bgPic), | |
202 | - icon: unref(bgPic), | |
203 | 205 | logo: unref(logoPic), |
204 | - }); | |
206 | + icon: unref(iconPic), | |
207 | + background: unref(bgPic), | |
208 | + }; | |
209 | + await updatePlatForm(newFieldValue); | |
205 | 210 | compState.value.loading = false; |
206 | 211 | createMessage.success('保存信息成功'); |
212 | + | |
213 | + setPlatFormInfo(newFieldValue); | |
207 | 214 | } catch (e) { |
208 | 215 | createMessage.error('保存信息失败'); |
209 | 216 | } |
210 | 217 | }; |
218 | + // 设置平台信息 | |
219 | + function setPlatFormInfo(newFieldValue) { | |
220 | + // 保存store | |
221 | + userStore.setPlatInfo(newFieldValue); | |
222 | + // 保存本地缓存 | |
223 | + storage.set('platInfo', newFieldValue); | |
224 | + } | |
211 | 225 | |
212 | 226 | onMounted(async () => { |
213 | 227 | const res = await getPlatForm(); | ... | ... |
1 | 1 | <template> |
2 | 2 | <div class="card"> |
3 | - <Card :bordered="false" class="card"> <BasicForm @register="registerForm" /></Card> | |
3 | + <Card :bordered="false" class="card"> | |
4 | + <BasicForm @register="registerForm"> | |
5 | + <template #qrcode> | |
6 | + <Upload | |
7 | + name="avatar" | |
8 | + list-type="picture-card" | |
9 | + class="avatar-uploader" | |
10 | + :show-upload-list="false" | |
11 | + :customRequest="customUploadqrcodePic" | |
12 | + :before-upload="beforeUploadqrcodePic" | |
13 | + > | |
14 | + <img v-if="qrcodePic" :src="qrcodePic" alt="avatar" /> | |
15 | + <div v-else> | |
16 | + <div style="margin-top: 30px"> | |
17 | + <PlusOutlined style="font-size: 30px" /> | |
18 | + </div> | |
19 | + <div | |
20 | + class="ant-upload-text flex" | |
21 | + style="width: 280px; height: 130px; align-items: center" | |
22 | + > | |
23 | + 支持.PNG、.JPG、.SVG格式,建议尺寸为300px × 300px(及以上),大小不超过2M。</div | |
24 | + > | |
25 | + </div> | |
26 | + </Upload> | |
27 | + </template></BasicForm | |
28 | + ></Card | |
29 | + > | |
4 | 30 | <Loading v-bind="compState" /> |
5 | 31 | <a-button |
6 | 32 | @click="handleUpdateInfo" |
... | ... | @@ -14,17 +40,25 @@ |
14 | 40 | |
15 | 41 | <script lang="ts"> |
16 | 42 | import { defineComponent, onMounted, ref } from 'vue'; |
17 | - import { Card } from 'ant-design-vue'; | |
43 | + import { Card, Upload } from 'ant-design-vue'; | |
18 | 44 | import { BasicForm, useForm } from '/@/components/Form/index'; |
19 | 45 | import { schemas } from '../config/enterPriseInfo.config'; |
20 | 46 | import { getEnterPriseDetail, updateEnterPriseDetail } from '/@/api/oem/index'; |
21 | 47 | import { Loading } from '/@/components/Loading'; |
22 | 48 | import { useMessage } from '/@/hooks/web/useMessage'; |
49 | + import { getTownChild } from '/@/api/oem/index'; | |
50 | + import { useUserStore } from '/@/store/modules/user'; | |
51 | + import { createLocalStorage } from '/@/utils/cache'; | |
52 | + import { PlusOutlined } from '@ant-design/icons-vue'; | |
53 | + import type { FileItem } from '/@/components/Upload/src/typing'; | |
54 | + import { qrcodeUpload } from '/@/api/oem/index'; | |
23 | 55 | export default defineComponent({ |
24 | 56 | components: { |
25 | 57 | Card, |
26 | 58 | BasicForm, |
27 | 59 | Loading, |
60 | + Upload, | |
61 | + PlusOutlined, | |
28 | 62 | }, |
29 | 63 | setup() { |
30 | 64 | const compState = ref({ |
... | ... | @@ -32,7 +66,7 @@ |
32 | 66 | loading: false, |
33 | 67 | tip: '拼命加载中...', |
34 | 68 | }); |
35 | - const [registerForm, { getFieldsValue, setFieldsValue }] = useForm({ | |
69 | + const [registerForm, { getFieldsValue, setFieldsValue, updateSchema }] = useForm({ | |
36 | 70 | labelWidth: 80, |
37 | 71 | schemas, |
38 | 72 | showResetButton: false, |
... | ... | @@ -42,32 +76,204 @@ |
42 | 76 | }, |
43 | 77 | }); |
44 | 78 | const { createMessage } = useMessage(); |
79 | + | |
80 | + const qrcodePic = ref(); | |
81 | + const customUploadqrcodePic = async ({ file }) => { | |
82 | + if (beforeUploadqrcodePic(file)) { | |
83 | + const formData = new FormData(); | |
84 | + formData.append('file', file); | |
85 | + const response = await qrcodeUpload(formData); | |
86 | + if (response.fileStaticUri) { | |
87 | + qrcodePic.value = response.fileStaticUri; | |
88 | + } | |
89 | + } | |
90 | + }; | |
91 | + const beforeUploadqrcodePic = (file: FileItem) => { | |
92 | + const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'; | |
93 | + if (!isJpgOrPng) { | |
94 | + createMessage.error('只能上传图片文件!'); | |
95 | + } | |
96 | + const isLt2M = (file.size as number) / 1024 / 1024 < 2; | |
97 | + if (!isLt2M) { | |
98 | + createMessage.error('图片大小不能超过2MB!'); | |
99 | + } | |
100 | + return isJpgOrPng && isLt2M; | |
101 | + }; | |
102 | + // 更新 | |
45 | 103 | const handleUpdateInfo = async () => { |
46 | 104 | try { |
47 | 105 | compState.value.loading = true; |
48 | 106 | const fieldsValue = getFieldsValue(); |
49 | - await updateEnterPriseDetail({ | |
107 | + const newFieldValue = { | |
50 | 108 | ...fieldsValue, |
51 | 109 | codeProv: fieldsValue.nameProv, |
52 | 110 | codeCity: fieldsValue.nameCity, |
53 | 111 | codeCoun: fieldsValue.nameCoun, |
54 | 112 | codeTown: fieldsValue.nameTown, |
55 | - }); | |
113 | + qrCode: qrcodePic.value, | |
114 | + }; | |
115 | + console.log(fieldsValue); | |
116 | + console.log(newFieldValue); | |
117 | + await updateEnterPriseDetail(newFieldValue); | |
56 | 118 | compState.value.loading = false; |
57 | 119 | createMessage.success('更新信息成功'); |
120 | + setEnterPriseInfo(newFieldValue); | |
58 | 121 | } catch (e) { |
59 | 122 | createMessage.error('更新信息失败'); |
60 | 123 | } |
61 | 124 | }; |
125 | + | |
126 | + const userStore = useUserStore(); | |
127 | + const storage = createLocalStorage(); | |
128 | + | |
129 | + // 设置企业信息 | |
130 | + function setEnterPriseInfo(newFieldValue) { | |
131 | + // 保存store | |
132 | + userStore.setEnterPriseInfo(newFieldValue); | |
133 | + // 保存本地缓存 | |
134 | + storage.set('enterpriseInfo', newFieldValue); | |
135 | + } | |
136 | + | |
137 | + // 地区显示回显和数据联动 | |
138 | + async function updateCityData( | |
139 | + codeProv: string, | |
140 | + codeCity: string, | |
141 | + codeCoun: string, | |
142 | + codeTown: string | |
143 | + ) { | |
144 | + const nameCity = await getTownChild('codeProv', codeProv); | |
145 | + const nameCoun = await getTownChild('codeCity', codeCity); | |
146 | + const nameTown = await getTownChild('codeCoun', codeCoun); | |
147 | + nameCity.forEach((item) => { | |
148 | + item.label = item.nameCity; | |
149 | + item.value = item.codeCity; | |
150 | + }); | |
151 | + nameCoun.forEach((item) => { | |
152 | + item.label = item.nameCoun; | |
153 | + item.value = item.codeCoun; | |
154 | + }); | |
155 | + nameTown.forEach((item) => { | |
156 | + item.label = item.nameTown; | |
157 | + item.value = item.codeTown; | |
158 | + }); | |
159 | + setFieldsValue({ | |
160 | + nameProv: codeProv, | |
161 | + nameCity: codeCity, | |
162 | + nameCoun: codeCoun, | |
163 | + nameTown: codeTown, | |
164 | + }); | |
165 | + updateSchema({ | |
166 | + field: 'nameTown', | |
167 | + componentProps: { | |
168 | + options: nameTown, | |
169 | + }, | |
170 | + }); | |
171 | + updateSchema({ | |
172 | + field: 'nameCoun', | |
173 | + componentProps: { | |
174 | + options: nameCoun, | |
175 | + async onChange(value) { | |
176 | + if (value === undefined) { | |
177 | + setFieldsValue({ | |
178 | + nameTown: undefined, | |
179 | + }); | |
180 | + updateSchema({ | |
181 | + field: 'nameTown', | |
182 | + componentProps: { | |
183 | + options: [], | |
184 | + }, | |
185 | + }); | |
186 | + } | |
187 | + let nameTown = await getTownChild('codeCoun', value); | |
188 | + nameTown.forEach((item) => { | |
189 | + item.label = item.nameTown; | |
190 | + item.value = item.codeTown; | |
191 | + }); | |
192 | + setFieldsValue({ | |
193 | + nameTown: undefined, | |
194 | + }); | |
195 | + updateSchema({ | |
196 | + field: 'nameTown', | |
197 | + componentProps: { | |
198 | + placeholder: '请选择街道/城镇', | |
199 | + options: nameTown, | |
200 | + }, | |
201 | + }); | |
202 | + }, | |
203 | + }, | |
204 | + }); | |
205 | + updateSchema({ | |
206 | + field: 'nameCity', | |
207 | + componentProps: ({ formModel }) => { | |
208 | + return { | |
209 | + options: nameCity, | |
210 | + onChange: async (value) => { | |
211 | + let nameCoun = await getTownChild('codeCity', value); | |
212 | + if (value === undefined) { | |
213 | + formModel.nameCoun = undefined; // reset city value | |
214 | + formModel.nameTown = undefined; | |
215 | + nameCoun = []; | |
216 | + updateSchema({ | |
217 | + field: 'nameTown', | |
218 | + componentProps: { | |
219 | + options: [], | |
220 | + }, | |
221 | + }); | |
222 | + } | |
223 | + nameCoun.forEach((item) => { | |
224 | + item.label = item.nameCoun; | |
225 | + item.value = item.codeCoun; | |
226 | + }); | |
227 | + formModel.nameCoun = undefined; // reset city value | |
228 | + formModel.nameTown = undefined; | |
229 | + updateSchema({ | |
230 | + field: 'nameCoun', | |
231 | + componentProps: { | |
232 | + // 请选择区 | |
233 | + options: nameCoun, | |
234 | + async onChange(value) { | |
235 | + let nameTown = await getTownChild('codeCoun', value); | |
236 | + if (value === undefined) { | |
237 | + formModel.nameTown = undefined; | |
238 | + nameTown = []; | |
239 | + } | |
240 | + nameTown.forEach((item) => { | |
241 | + item.label = item.nameTown; | |
242 | + item.value = item.codeTown; | |
243 | + }); | |
244 | + | |
245 | + formModel.nameTown = undefined; | |
246 | + updateSchema({ | |
247 | + field: 'nameTown', | |
248 | + componentProps: { | |
249 | + placeholder: '请选择街道/城镇', | |
250 | + options: nameTown, | |
251 | + }, | |
252 | + }); | |
253 | + }, | |
254 | + }, | |
255 | + }); | |
256 | + }, | |
257 | + }; | |
258 | + }, | |
259 | + }); | |
260 | + } | |
261 | + | |
62 | 262 | onMounted(async () => { |
63 | 263 | const res = await getEnterPriseDetail(); |
264 | + updateCityData(res.codeProv, res.codeCity, res.codeCoun, res.codeTown); | |
64 | 265 | setFieldsValue(res); |
266 | + qrcodePic.value = res.qrCode; | |
267 | + console.log(res); | |
65 | 268 | }); |
66 | 269 | |
67 | 270 | return { |
68 | 271 | registerForm, |
69 | 272 | compState, |
273 | + qrcodePic, | |
70 | 274 | handleUpdateInfo, |
275 | + customUploadqrcodePic, | |
276 | + beforeUploadqrcodePic, | |
71 | 277 | }; |
72 | 278 | }, |
73 | 279 | }); | ... | ... |
... | ... | @@ -51,6 +51,7 @@ export const schemas: FormSchema[] = [ |
51 | 51 | field: 'synopsis', |
52 | 52 | component: 'InputTextArea', |
53 | 53 | label: '公司简介', |
54 | + | |
54 | 55 | colProps: { |
55 | 56 | span: 24, |
56 | 57 | }, |
... | ... | @@ -116,6 +117,9 @@ export const schemas: FormSchema[] = [ |
116 | 117 | }, |
117 | 118 | }); |
118 | 119 | } |
120 | + formModel.nameCity = undefined; // reset city value | |
121 | + formModel.nameCoun = undefined; | |
122 | + formModel.nameTown = undefined; | |
119 | 123 | updateSchema({ |
120 | 124 | field: 'nameCity', |
121 | 125 | componentProps: () => { |
... | ... | @@ -146,14 +150,20 @@ export const schemas: FormSchema[] = [ |
146 | 150 | // 请选择区 |
147 | 151 | options: nameCoun, |
148 | 152 | async onChange(value) { |
149 | - let nameTown = await getTownChild('codeCoun', value); | |
153 | + const nameTown = await getTownChild('codeCoun', value); | |
150 | 154 | nameTown.forEach((item) => { |
151 | 155 | item.label = item.nameTown; |
152 | 156 | item.value = item.codeTown; |
153 | 157 | }); |
154 | 158 | if (value === undefined) { |
155 | 159 | formModel.nameTown = undefined; |
156 | - nameTown = []; | |
160 | + updateSchema({ | |
161 | + field: 'nameTown', | |
162 | + componentProps: { | |
163 | + placeholder: '请选择街道/城镇', | |
164 | + options: [], | |
165 | + }, | |
166 | + }); | |
157 | 167 | } |
158 | 168 | updateSchema({ |
159 | 169 | field: 'nameTown', |
... | ... | @@ -177,11 +187,10 @@ export const schemas: FormSchema[] = [ |
177 | 187 | field: 'nameCity', |
178 | 188 | component: 'Select', |
179 | 189 | label: '', |
180 | - | |
181 | 190 | colProps: { |
182 | 191 | span: 5, |
183 | 192 | style: { |
184 | - marginLeft: '-80px', | |
193 | + marginLeft: '-5rem', | |
185 | 194 | }, |
186 | 195 | }, |
187 | 196 | }, |
... | ... | @@ -213,13 +222,6 @@ export const schemas: FormSchema[] = [ |
213 | 222 | placeholder: '请选择街道/城镇', |
214 | 223 | }, |
215 | 224 | }, |
216 | - | |
217 | - // { | |
218 | - // field: 'nameProv', | |
219 | - // label: '所在城市', | |
220 | - // component: 'Cascader', | |
221 | - // }, | |
222 | - | |
223 | 225 | { |
224 | 226 | field: 'address', |
225 | 227 | component: 'Input', |
... | ... | @@ -254,4 +256,13 @@ export const schemas: FormSchema[] = [ |
254 | 256 | placeholder: '请输入联系电话', |
255 | 257 | }, |
256 | 258 | }, |
259 | + { | |
260 | + field: 'qrcode', | |
261 | + label: '二维码', | |
262 | + component: 'Input', | |
263 | + colProps: { | |
264 | + span: 24, | |
265 | + }, | |
266 | + slot: 'qrcode', | |
267 | + }, | |
257 | 268 | ]; | ... | ... |
... | ... | @@ -23,7 +23,7 @@ |
23 | 23 | import EnterpriseInfo from './components/EnterpriseInfo.vue'; |
24 | 24 | import CVIDraw from './components/CVIDraw.vue'; |
25 | 25 | import AppDraw from './components/AppDraw.vue'; |
26 | - const activeKey = ref('APP定制'); | |
26 | + const activeKey = ref('企业信息'); | |
27 | 27 | </script> |
28 | 28 | |
29 | 29 | <style lang="less" scoped> | ... | ... |