Commit 5f3f30bc1469a6dedce558ba8bb521594ac6673a

Authored by fengistao
2 parents 901b26d2 f40541a3

Merge branch 'main' into ft-dev

Showing 42 changed files with 620 additions and 354 deletions
... ... @@ -5,4 +5,4 @@ VITE_PORT = 8083
5 5 VITE_GLOB_APP_TITLE = Yunteng IOT
6 6
7 7 # spa shortname
8   -VITE_GLOB_APP_SHORT_NAME = Yunteng IOT
  8 +# VITE_GLOB_APP_SHORT_NAME = Yunteng IOT
... ...
... ... @@ -45,7 +45,6 @@
45 45 "cropperjs": "^1.5.12",
46 46 "crypto-js": "^4.1.1",
47 47 "echarts": "^5.1.2",
48   - "emit": "^0.0.2",
49 48 "intro.js": "^4.1.0",
50 49 "jwt-decode": "^3.1.2",
51 50 "lodash-es": "^4.17.21",
... ...
  1 +import { defHttp } from '/@/utils/http/axios';
  2 +
  3 +export const getDeviceProfile = () => {
  4 + return defHttp.get({
  5 + url: '/deviceProfile/me',
  6 + });
  7 +};
... ...
... ... @@ -90,3 +90,8 @@ export const updateAppDesign = (data) => {
90 90 data,
91 91 });
92 92 };
  93 +
  94 +// 二维码上传
  95 +export const qrcodeUpload = (file) => {
  96 + return defHttp.post<FileUploadResponse>({ url: API.BaseUploadUrl, params: file });
  97 +};
... ...
... ... @@ -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>
... ...
... ... @@ -132,10 +132,6 @@ export const formSchema: FormSchema[] = [
132 132 field: 'id',
133 133 label: '',
134 134 component: 'Input',
135   - componentProps: {
136   - style: {
137   - display: 'none',
138   - },
139   - },
  135 + show: false,
140 136 },
141 137 ];
... ...
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>>();
... ...
... ... @@ -140,7 +140,7 @@
140 140 }
141 141
142 142 function goLogin() {
143   - userStore.logout(true);
  143 + userStore.logout();
144 144 lockStore.resetLockInfo();
145 145 }
146 146
... ...
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>
... ...
1 1 <template>
2   - <h2 class="mb-3 text-2xl font-bold text-center xl:text-3xl enter-x xl:text-left">
  2 + <h2
  3 + class="mb-3 text-2xl font-bold text-center xl:text-3xl enter-x xl:text-left flex justify-center"
  4 + >
3 5 {{ getFormTitle }}
4 6 </h2>
5 7 </template>
... ...
... ... @@ -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>
... ...