Commit 4c79e9e45b2da81875172d7d1b939e95b4922735

Authored by fengwotao
2 parents 1f87bc38 1d60a05b

Merge branch 'main_dev' into ft

... ... @@ -16,6 +16,7 @@ import {
16 16 UpdateDataComponentParams,
17 17 } from './model';
18 18 import { defHttp } from '/@/utils/http/axios';
  19 +import { isShareMode } from '/@/views/sys/share/hook';
19 20
20 21 enum DataBoardUrl {
21 22 GET_DATA_BOARD = '/data_board',
... ... @@ -224,7 +225,7 @@ export const sendCommandOneway = (params: SendCommandParams) => {
224 225 url: `${SendCommand.ONEWAY}/${params.deviceId}`,
225 226 params: params.value,
226 227 },
227   - { joinPrefix: false }
  228 + { joinPrefix: false, withShareToken: isShareMode() }
228 229 );
229 230 };
230 231
... ...
1 1 import { defHttp } from '/@/utils/http/axios';
2 2 import { ViewTypeEnum } from '/@/views/sys/share/config/config';
  3 +import { isShareMode } from '/@/views/sys/share/hook';
3 4
4 5 enum Api {
5 6 CHECK = '/share/check',
... ... @@ -8,9 +9,14 @@ enum Api {
8 9 }
9 10
10 11 export const checkShareAccessToken = (type: ViewTypeEnum, id: string) => {
11   - return defHttp.get<Record<'data', boolean>>({
12   - url: `${Api.CHECK}/${type}/${id}`,
13   - });
  12 + return defHttp.get<Record<'data', boolean>>(
  13 + {
  14 + url: `${Api.CHECK}/${type}/${id}`,
  15 + },
  16 + {
  17 + withShareToken: isShareMode(),
  18 + }
  19 + );
14 20 };
15 21
16 22 export const sharePageLogin = (publicId: string) => {
... ... @@ -21,14 +27,20 @@ export const sharePageLogin = (publicId: string) => {
21 27 },
22 28 {
23 29 joinPrefix: false,
  30 + withShareToken: isShareMode(),
24 31 }
25 32 );
26 33 };
27 34
28 35 export const getShareContent = (record: Record<'accessCredentials' | 'id', string>) => {
29 36 const { id, accessCredentials } = record;
30   - return defHttp.get({
31   - url: `${Api.SHARE_CONTENT}/${ViewTypeEnum.DATA_BOARD}/share_data/${id}`,
32   - params: { accessCredentials },
33   - });
  37 + return defHttp.get(
  38 + {
  39 + url: `${Api.SHARE_CONTENT}/${ViewTypeEnum.DATA_BOARD}/share_data/${id}`,
  40 + params: { accessCredentials },
  41 + },
  42 + {
  43 + withShareToken: isShareMode(),
  44 + }
  45 + );
34 46 };
... ...
... ... @@ -47,6 +47,7 @@ export interface TaskRecordType extends CreateTaskRecordType {
47 47 enabled: boolean;
48 48 state: number;
49 49 lastExecuteTime?: number;
  50 + lastExecuteStr?: string;
50 51 tkDeviceTaskCenter?: {
51 52 allowState: number;
52 53 taskCenterId: string;
... ...
1 1 <script lang="ts" setup>
2 2 import { ReloadOutlined } from '@ant-design/icons-vue';
3   - import { Button, List, Tooltip } from 'ant-design-vue';
  3 + import { Button, List, Space, Tooltip } from 'ant-design-vue';
4 4 import { reactive } from 'vue';
5 5 import { ref } from 'vue';
  6 + import { BasicForm, useForm } from '../../Form';
  7 + import { basicListProps } from './props';
  8 + import { ExtractPropTypes } from 'vue';
  9 + defineProps<ExtractPropTypes<typeof basicListProps>>();
  10 +
  11 + const [registerForm] = useForm({
  12 + schemas: [
  13 + {
  14 + field: '123',
  15 + label: 'test',
  16 + component: 'Input',
  17 + },
  18 + ],
  19 + labelWidth: 100,
  20 + layout: 'inline',
  21 + baseColProps: { span: 8 },
  22 + showAdvancedButton: true,
  23 + compact: true,
  24 + });
6 25
7 26 const listElRef = ref<Nullable<ComponentElRef>>(null);
8 27
9   - const pagination = reactive({});
  28 + const pagination = reactive({ size: 'small' });
10 29
11 30 const loading = ref(false);
12 31
... ... @@ -16,31 +35,48 @@
16 35 </script>
17 36
18 37 <template>
19   - <section class="bg-light-50 my-4 p-4 x dark:text-gray-300 dark:bg-dark-900">
20   - <List
21   - ref="listElRef"
22   - :dataSource="dataSource"
23   - :pagination="pagination"
24   - :grid="{ gutter: 16, xs: 1, sm: 1, md: 1, lg: 2, xl: 2, xxl: 3, column: 3 }"
25   - :loading="loading"
26   - >
27   - <template #header>
28   - <section class="flex justify-between gap-4 min-h-12 items-center">
29   - <div class="text-lg font-semibold">
30   - <span>任务列表</span>
31   - </div>
32   - <Tooltip title="刷新">
33   - <Button type="primary" @click="getDataSource">
34   - <ReloadOutlined :spin="loading" />
35   - </Button>
36   - </Tooltip>
37   - </section>
38   - </template>
39   - <template #renderItem="{ item }">
40   - <List.Item :key="item.id">
41   - <slot name="item" :item="item"></slot>
42   - </List.Item>
43   - </template>
44   - </List>
  38 + <section class="basic-list-container">
  39 + <section class="mb-4 bg-light-50 p-2 x dark:text-gray-300 dark:bg-dark-900">
  40 + <BasicForm @register="registerForm" />
  41 + </section>
  42 + <section class="bg-light-50 p-2 x dark:text-gray-300 dark:bg-dark-900">
  43 + <List
  44 + ref="listElRef"
  45 + :dataSource="dataSource"
  46 + :pagination="pagination"
  47 + :grid="{ gutter: 16, xs: 1, sm: 1, md: 1, lg: 2, xl: 2, xxl: 3, column: 3 }"
  48 + :loading="loading"
  49 + >
  50 + <template #header>
  51 + <section class="flex px-5 justify-between gap-4 min-h-12 items-center">
  52 + <div class="text-lg font-semibold">
  53 + <span>任务列表</span>
  54 + </div>
  55 + <Space>
  56 + <slot name="toolbar"></slot>
  57 + <Tooltip title="刷新">
  58 + <Button type="primary" @click="getDataSource">
  59 + <ReloadOutlined :spin="loading" />
  60 + </Button>
  61 + </Tooltip>
  62 + </Space>
  63 + </section>
  64 + </template>
  65 + <template #renderItem="{ item }">
  66 + <List.Item :key="item.id">
  67 + <slot name="item" :item="item"></slot>
  68 + </List.Item>
  69 + </template>
  70 + </List>
  71 + </section>
45 72 </section>
46 73 </template>
  74 +
  75 +<style lang="less" scoped>
  76 + .basic-list-container {
  77 + :deep(.ant-list-header) {
  78 + padding-top: 0;
  79 + padding-bottom: 8px;
  80 + }
  81 + }
  82 +</style>
... ...
  1 +import type { ComputedRef, Slots } from 'vue';
  2 +import type { BasicTableProps, FetchParams } from '../types/table';
  3 +import { unref, computed } from 'vue';
  4 +import type { FormProps } from '/@/components/Form';
  5 +import { isFunction } from '/@/utils/is';
  6 +
  7 +export function useTableForm(
  8 + propsRef: ComputedRef<BasicTableProps>,
  9 + slots: Slots,
  10 + fetch: (opt?: FetchParams | undefined) => Promise<void>,
  11 + getLoading: ComputedRef<boolean | undefined>
  12 +) {
  13 + const getFormProps = computed((): Partial<FormProps> => {
  14 + const { formConfig } = unref(propsRef);
  15 + const { submitButtonOptions } = formConfig || {};
  16 + return {
  17 + showAdvancedButton: true,
  18 + ...formConfig,
  19 + submitButtonOptions: { loading: unref(getLoading), ...submitButtonOptions },
  20 + compact: true,
  21 + };
  22 + });
  23 +
  24 + const getFormSlotKeys: ComputedRef<string[]> = computed(() => {
  25 + const keys = Object.keys(slots);
  26 + return keys
  27 + .map((item) => (item.startsWith('form-') ? item : null))
  28 + .filter((item) => !!item) as string[];
  29 + });
  30 +
  31 + function replaceFormSlotKey(key: string) {
  32 + if (!key) return '';
  33 + return key?.replace?.(/form\-/, '') ?? '';
  34 + }
  35 +
  36 + function handleSearchInfoChange(info: Recordable) {
  37 + const { handleSearchInfoFn } = unref(propsRef);
  38 + if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) {
  39 + info = handleSearchInfoFn(info) || info;
  40 + }
  41 + fetch({ searchInfo: info, page: 1 });
  42 + }
  43 +
  44 + return {
  45 + getFormProps,
  46 + replaceFormSlotKey,
  47 + getFormSlotKeys,
  48 + handleSearchInfoChange,
  49 + };
  50 +}
... ...
1   -import { ComponentPropsOptions } from 'vue';
  1 +import { FormProps } from '../../Form';
2 2
3   -export const props = {
  3 +export const basicListProps = {
  4 + immediate: {
  5 + type: Boolean,
  6 + default: true,
  7 + },
  8 + searchInfo: {
  9 + type: Object as PropType<Recordable>,
  10 + },
  11 + formConfig: {
  12 + type: Object as PropType<Partial<FormProps>>,
  13 + default: null,
  14 + },
4 15 title: {
5 16 type: String,
6 17 },
7   -} as ComponentPropsOptions;
  18 + titleHelpMessage: {
  19 + type: [String, Array] as PropType<string | string[]>,
  20 + },
  21 + autoCreateKey: {
  22 + type: Boolean,
  23 + default: true,
  24 + },
  25 + api: {
  26 + type: Function as PropType<Fn<any, Promise<any>>>,
  27 + },
  28 + beforeFetch: {
  29 + type: Function as PropType<Fn>,
  30 + },
  31 + afterFetch: {
  32 + type: Function as PropType<Fn>,
  33 + },
  34 + handleSearchInfoFn: {
  35 + type: Function as PropType<Fn>,
  36 + },
  37 +};
... ...
  1 +import { FormProps } from 'ant-design-vue/es/form/Form';
  2 +
  3 +export interface BasicListProps {
  4 + /**
  5 + * @description 立即执行
  6 + */
  7 + immediate?: boolean;
  8 +
  9 + /**
  10 + * @description 额外的请求参数
  11 + */
  12 + searchInfo?: Recordable;
  13 +
  14 + /**
  15 + * @description 搜索表单
  16 + */
  17 + formConfig?: Partial<FormProps>;
  18 +
  19 + /**
  20 + * @description 列表名
  21 + */
  22 + title?: string;
  23 +
  24 + /**
  25 + * @description 列表标题帮助信息
  26 + */
  27 + titleHelpMessage?: string | string[];
  28 +
  29 + /**
  30 + * @description 自动创建key
  31 + */
  32 + autoCreateKey?: boolean;
  33 +
  34 + /**
  35 + * @description 请求接口
  36 + * @param args
  37 + * @returns
  38 + */
  39 + api?: (...args: any) => Promise<any>;
  40 +
  41 + /**
  42 + * @description 请求前对参数处理
  43 + */
  44 + beforeFetch?: Fn;
  45 +
  46 + /**
  47 + * @description 请求后对结果处理
  48 + */
  49 + afterFetch?: Fn;
  50 +
  51 + /**
  52 + * @description 请求前处理搜索参数
  53 + */
  54 + handleSearchInfoFn?: Fn;
  55 +}
... ...
... ... @@ -5,6 +5,10 @@ export const JWT_TOKEN_KEY = 'JWT_TOKEN';
5 5
6 6 export const REFRESH_TOKEN_KEY = 'REFRESH_TOKEN';
7 7
  8 +export const SHARE_JWT_TOKEN_KEY = 'SHARE_JWT_TOKEN';
  9 +
  10 +export const SHARE_REFRESH_TOKEN_KEY = 'SHARE_REFRESH_TOKEN';
  11 +
8 12 export const LOCALE_KEY = 'LOCALE__';
9 13
10 14 // user info key
... ...
... ... @@ -224,7 +224,7 @@ export const usePermissionStore = defineStore({
224 224 // !Simulate to obtain permission codes from the background,
225 225 // this function may only need to be executed once, and the actual project can be put at the right time by itself
226 226 let routeList: AppRouteRecordRaw[] = [];
227   - const userInfo: any = getAuthCache(USER_INFO_KEY);
  227 + const userInfo: any = getAuthCache(USER_INFO_KEY) || { roles: [] };
228 228 const filterMenu = (allMenuList, menuIdsList) => {
229 229 return allMenuList
230 230 .filter((item) => {
... ...
... ... @@ -4,7 +4,14 @@ import { defineStore } from 'pinia';
4 4 import { store } from '/@/store';
5 5 import { RoleEnum } from '/@/enums/roleEnum';
6 6 import { PageEnum } from '/@/enums/pageEnum';
7   -import { JWT_TOKEN_KEY, REFRESH_TOKEN_KEY, ROLES_KEY, USER_INFO_KEY } from '/@/enums/cacheEnum';
  7 +import {
  8 + JWT_TOKEN_KEY,
  9 + REFRESH_TOKEN_KEY,
  10 + ROLES_KEY,
  11 + SHARE_JWT_TOKEN_KEY,
  12 + SHARE_REFRESH_TOKEN_KEY,
  13 + USER_INFO_KEY,
  14 +} from '/@/enums/cacheEnum';
8 15 import { getAuthCache, setAuthCache } from '/@/utils/auth';
9 16 import {
10 17 LoginParams,
... ... @@ -33,6 +40,8 @@ interface UserState {
33 40 lastUpdateTime: number;
34 41 jwtToken?: string;
35 42 refreshToken?: string;
  43 + shareJwtToken?: string;
  44 + shareRefreshToken?: string;
36 45 outTarget?: string;
37 46 }
38 47
... ... @@ -100,6 +109,13 @@ export const useUserStore = defineStore({
100 109 setAuthCache(JWT_TOKEN_KEY, jwtToken);
101 110 setAuthCache(REFRESH_TOKEN_KEY, refreshToken);
102 111 },
  112 +
  113 + storeShareToken(jwtToken: string, refreshToken: string) {
  114 + this.shareJwtToken = jwtToken;
  115 + this.shareRefreshToken = refreshToken;
  116 + setAuthCache(SHARE_JWT_TOKEN_KEY, jwtToken);
  117 + setAuthCache(SHARE_REFRESH_TOKEN_KEY, refreshToken);
  118 + },
103 119 setToken(info: string | undefined) {
104 120 this.jwtToken = info;
105 121 setAuthCache(JWT_TOKEN_KEY, info);
... ... @@ -232,6 +248,18 @@ export const useUserStore = defineStore({
232 248 }
233 249 },
234 250
  251 + async doShareRefresh() {
  252 + try {
  253 + const req = { refreshToken: this.shareRefreshToken } as RefreshTokenParams;
  254 + const data = await doRefreshToken(req);
  255 + const { token, refreshToken } = data;
  256 + this.storeToken(token, refreshToken);
  257 + } catch (error) {
  258 + window.location.reload();
  259 + throw error;
  260 + }
  261 + },
  262 +
235 263 /**
236 264 * @description: Confirm before logging out
237 265 */
... ...
1 1 import { Persistent, BasicKeys } from '/@/utils/cache/persistent';
2   -import { CacheTypeEnum } from '/@/enums/cacheEnum';
  2 +import { CacheTypeEnum, SHARE_JWT_TOKEN_KEY, SHARE_REFRESH_TOKEN_KEY } from '/@/enums/cacheEnum';
3 3 import projectSetting from '/@/settings/projectSetting';
4 4 import { JWT_TOKEN_KEY, REFRESH_TOKEN_KEY } from '/@/enums/cacheEnum';
5 5
... ... @@ -26,3 +26,11 @@ export function getJwtToken() {
26 26 export function getRefreshToken() {
27 27 return getAuthCache(REFRESH_TOKEN_KEY);
28 28 }
  29 +
  30 +export function getShareJwtToken() {
  31 + return getAuthCache(SHARE_JWT_TOKEN_KEY);
  32 +}
  33 +
  34 +export function getShareRefreshToken() {
  35 + return getAuthCache(SHARE_REFRESH_TOKEN_KEY);
  36 +}
... ...
... ... @@ -17,6 +17,8 @@ import {
17 17 APP_SESSION_CACHE_KEY,
18 18 MULTIPLE_TABS_KEY,
19 19 MENU_LIST,
  20 + SHARE_JWT_TOKEN_KEY,
  21 + SHARE_REFRESH_TOKEN_KEY,
20 22 } from '/@/enums/cacheEnum';
21 23 import { DEFAULT_CACHE_TIME } from '/@/settings/encryptionSetting';
22 24 import { toRaw } from 'vue';
... ... @@ -33,6 +35,8 @@ interface BasicStore {
33 35 [PROJ_CFG_KEY]: ProjectConfig;
34 36 [MULTIPLE_TABS_KEY]: RouteLocationNormalized[];
35 37 [MENU_LIST]: any[];
  38 + [SHARE_JWT_TOKEN_KEY]: string;
  39 + [SHARE_REFRESH_TOKEN_KEY]: string;
36 40 }
37 41
38 42 type LocalStore = BasicStore;
... ...
... ... @@ -80,7 +80,6 @@ export class VAxios {
80 80 private refreshTokenBeforeReq(doRefreshTokenApi: () => Promise<unknown>): Promise<unknown> {
81 81 // 创建一个未完成的promise,把改变状态的resolve方法交给请求token结束后执行
82 82 const promise = new Promise((resolve) => {
83   - console.log('等待新token');
84 83 // 等待队列放的是一个回调函数,来延迟resolve的执行,以此控制promise状态的改变
85 84 this.waitingQueue.push(() => resolve(null));
86 85 });
... ... @@ -88,7 +87,6 @@ export class VAxios {
88 87 this.refreshing = true;
89 88 // 模拟请求刷新Token接口,当接口返回数据时执行then方法 TODO 添加catch捕获异常
90 89 doRefreshTokenApi().then(() => {
91   - console.log('刷新token成功,放行队列中的请求', this.waitingQueue.length);
92 90 this.refreshing = false;
93 91 this.waitingQueue.forEach((cb) => cb());
94 92 this.waitingQueue.length = 0;
... ... @@ -117,12 +115,19 @@ export class VAxios {
117 115 this.axiosInstance.interceptors.request.use(async (config: AxiosRequestConfig) => {
118 116 // If cancel repeat request is turned on, then cancel repeat request is prohibited
119 117 const userStore = useUserStore();
120   - if (userStore && userStore.jwtToken) {
  118 + if (userStore) {
121 119 try {
122   - const res = jwt_decode(userStore.jwtToken) as JwtModel;
123   - const currentTime = (new Date().getTime() + (config.timeout || 0)) / 1000;
124   - if (currentTime >= res.exp && this.isNeedTokenURL(config.url)) {
125   - await this.refreshTokenBeforeReq(userStore.doRefresh);
  120 + const { requestOptions = {} } = config;
  121 + const { withShareToken, withToken } = requestOptions;
  122 + const token = withShareToken && withToken ? userStore.shareJwtToken : userStore.jwtToken;
  123 + const doRefresh =
  124 + withShareToken && withToken ? userStore.doShareRefresh : userStore.doRefresh;
  125 + if (token) {
  126 + const res = jwt_decode(token) as JwtModel;
  127 + const currentTime = (new Date().getTime() + (config.timeout || 0)) / 1000;
  128 + if (currentTime >= res.exp && this.isNeedTokenURL(config.url!)) {
  129 + await this.refreshTokenBeforeReq(doRefresh);
  130 + }
126 131 }
127 132 } catch (error) {
128 133 userStore.logout();
... ... @@ -244,7 +249,6 @@ export class VAxios {
244 249 const { requestOptions } = this.options;
245 250
246 251 const opt: RequestOptions = Object.assign({}, requestOptions, options);
247   -
248 252 const { beforeRequestHook, requestCatchHook, transformRequestHook } = transform || {};
249 253 if (beforeRequestHook && isFunction(beforeRequestHook)) {
250 254 conf = beforeRequestHook(conf, opt);
... ...
1 1 /**
2 2 * Data processing class, can be configured according to the project
3 3 */
4   -import type { AxiosRequestConfig, AxiosResponse } from 'axios';
  4 +import type { AxiosRequestConfig as OriginalAxiosRequestConfig, AxiosResponse } from 'axios';
5 5 import type { RequestOptions, Result } from '/#/axios';
6 6
7 7 export interface CreateAxiosOptions extends AxiosRequestConfig {
... ... @@ -10,6 +10,7 @@ export interface CreateAxiosOptions extends AxiosRequestConfig {
10 10 transform?: AxiosTransform;
11 11 requestOptions?: RequestOptions;
12 12 }
  13 +export type AxiosRequestConfig = OriginalAxiosRequestConfig & { requestOptions?: RequestOptions };
13 14
14 15 export abstract class AxiosTransform {
15 16 /**
... ...
... ... @@ -10,7 +10,7 @@ import { useGlobSetting } from '/@/hooks/setting';
10 10 import { useMessage } from '/@/hooks/web/useMessage';
11 11 import { RequestEnum, ContentTypeEnum } from '/@/enums/httpEnum';
12 12 import { isString } from '/@/utils/is';
13   -import { getJwtToken } from '/@/utils/auth';
  13 +import { getJwtToken, getShareJwtToken } from '/@/utils/auth';
14 14 import { setObjToUrlParams, deepMerge } from '/@/utils';
15 15 import { useErrorLogStoreWithOut } from '/@/store/modules/errorLog';
16 16 import { useI18n } from '/@/hooks/web/useI18n';
... ... @@ -92,12 +92,23 @@ const transform: AxiosTransform = {
92 92 */
93 93 requestInterceptors: (config, options) => {
94 94 // 请求之前处理config
95   - const token = getJwtToken();
96   - if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
97   - // jwt token
98   - config.headers['X-Authorization'] = options.authenticationScheme
99   - ? `${options.authenticationScheme} ${token}`
100   - : token;
  95 + const { requestOptions } = config;
  96 + const { withShareToken } = requestOptions || {};
  97 + const { requestOptions: { withToken } = {} } = options;
  98 + if (withToken !== false) {
  99 + const shareToken = getShareJwtToken();
  100 + if (withShareToken && shareToken) {
  101 + config.headers['X-Authorization'] = options.authenticationScheme
  102 + ? `${options.authenticationScheme} ${shareToken}`
  103 + : shareToken;
  104 + } else {
  105 + const token = getJwtToken();
  106 + if (token) {
  107 + config.headers['X-Authorization'] = options.authenticationScheme
  108 + ? `${options.authenticationScheme} ${token}`
  109 + : token;
  110 + }
  111 + }
101 112 }
102 113 return config;
103 114 },
... ... @@ -194,7 +205,7 @@ function createAxios(opt?: Partial<CreateAxiosOptions>) {
194 205 ignoreCancelToken: true,
195 206 // 是否携带token
196 207 withToken: true,
197   - },
  208 + } as RequestOptions,
198 209 },
199 210 opt || {}
200 211 )
... ...
... ... @@ -25,7 +25,7 @@ export const schemas: FormSchema[] = [
25 25 {
26 26 field: FieldsEnum.ACCESS_CREDENTIALS,
27 27 label: '访问凭证',
28   - component: 'Input',
  28 + component: 'InputPassword',
29 29 ifShow: ({ model }) => model[FieldsEnum.IS_SHARE],
30 30 componentProps: {
31 31 maxLength: 64,
... ...
  1 +export const isShareMode = () => {
  2 + const sharePageReg = /^\/share\/[^/]+\/[^/]+\/[^/]+$/;
  3 + const { pathname } = location;
  4 + return sharePageReg.test(pathname);
  5 +};
... ...
1 1 <script lang="ts" setup>
2 2 import { onMounted } from 'vue';
3   - import { BasicModal, useModal } from '/@/components/Modal';
4   - import { Spin } from 'ant-design-vue';
  3 + import { Spin, Modal, Button, Input, Row, Col } from 'ant-design-vue';
5 4 import { ref } from 'vue';
6 5 import { useRoute } from 'vue-router';
7 6 import { checkShareAccessToken, sharePageLogin, getShareContent } from '/@/api/sys/share';
... ... @@ -16,7 +15,9 @@
16 15 const contentData = ref<any>();
17 16 const canLoadComponent = ref(false);
18 17
19   - const [register, { openModal }] = useModal();
  18 + const modelVisable = ref(false);
  19 +
  20 + // const [register, { openModal }] = useModal();
20 21
21 22 const [registerForm, { getFieldsValue }] = useForm({
22 23 schemas: [
... ... @@ -24,11 +25,15 @@
24 25 field: FieldsEnum.ACCESS_CREDENTIALS,
25 26 component: 'Input',
26 27 label: '访问令牌',
  28 + slot: 'tokenInput',
  29 + componentProps: {
  30 + placeholder: '请输入访问令牌',
  31 + },
27 32 },
28 33 ],
29 34 showActionButtonGroup: false,
30   - layout: 'inline',
31   - labelWidth: 100,
  35 + layout: 'vertical',
  36 + baseColProps: { span: 24 },
32 37 });
33 38
34 39 const userStore = useUserStore();
... ... @@ -37,7 +42,7 @@
37 42 const { params } = ROUTE;
38 43 const { publicId } = params as Partial<ShareRouteParams>;
39 44 const { token, refreshToken } = await sharePageLogin(publicId!);
40   - userStore.storeToken(token, refreshToken);
  45 + userStore.storeShareToken(token, refreshToken);
41 46 };
42 47
43 48 const getCheckNeedAccessToken = async () => {
... ... @@ -45,7 +50,7 @@
45 50 loading.value = true;
46 51 const { viewType, id } = params as Partial<ShareRouteParams>;
47 52 const { data } = await checkShareAccessToken(viewType!, id!);
48   - data ? openModal(data) : await getContentData();
  53 + data ? (modelVisable.value = true) : await getContentData();
49 54 };
50 55
51 56 const getContentLoading = ref(false);
... ... @@ -59,7 +64,8 @@
59 64 contentData.value = result;
60 65 loading.value = false;
61 66 canLoadComponent.value = true;
62   - openModal(false);
  67 + // openModal(false);
  68 + modelVisable.value = false;
63 69 } catch (error) {
64 70 } finally {
65 71 getContentLoading.value = false;
... ... @@ -77,15 +83,64 @@
77 83 </script>
78 84
79 85 <template>
80   - <BasicModal
81   - @register="register"
82   - title="公开"
83   - @ok="handleSubmit"
84   - :showCancelBtn="false"
85   - :okButtonProps="{ loading: getContentLoading }"
  86 + <Modal
  87 + v-model:visible="modelVisable"
  88 + centered
  89 + :title="null"
  90 + :close-icon="null"
  91 + width="350px"
  92 + :footer="null"
  93 + wrapClassName="share-page-token-modal"
86 94 >
87   - <BasicForm @register="registerForm" />
88   - </BasicModal>
  95 + <section class="w-full h-full flex flex-col p-8 justify-center items-center">
  96 + <BasicForm @register="registerForm" class="w-full" @keyup.enter="handleSubmit">
  97 + <template #tokenInput="{ field, model }">
  98 + <Input.Group>
  99 + <Row>
  100 + <Col :span="18">
  101 + <Input.Password v-model:value="model[field]" placeholder="请输入访问令牌" />
  102 + </Col>
  103 + <Col :span="6">
  104 + <Button
  105 + class="w-full !flex justify-center items-center"
  106 + :loading="getContentLoading"
  107 + type="primary"
  108 + @click="handleSubmit"
  109 + >
  110 + <svg
  111 + t="1682068997810"
  112 + class="icon"
  113 + viewBox="0 0 1024 1024"
  114 + version="1.1"
  115 + xmlns="http://www.w3.org/2000/svg"
  116 + p-id="79416"
  117 + width="24"
  118 + height="24"
  119 + >
  120 + <path
  121 + d="M512 928H128c-19.2 0-32-12.8-32-32V128c0-19.2 12.8-32 32-32h384c19.2 0 32 12.8 32 32s-12.8 32-32 32H160v704h352c19.2 0 32 12.8 32 32s-12.8 32-32 32z"
  122 + fill="#fff"
  123 + p-id="79417"
  124 + />
  125 + <path
  126 + d="M534.4 736c-9.6 0-16-3.2-22.4-9.6l-192-192c-12.8-12.8-12.8-32 0-44.8l192-192c12.8-12.8 32-12.8 44.8 0 12.8 12.8 12.8 32 0 44.8L387.2 512l169.6 169.6c12.8 12.8 12.8 32 0 44.8-6.4 6.4-16 9.6-22.4 9.6z"
  127 + fill="#fff"
  128 + p-id="79418"
  129 + />
  130 + <path
  131 + d="M896 544H342.4c-19.2 0-32-12.8-32-32s12.8-32 32-32H896c19.2 0 32 12.8 32 32s-12.8 32-32 32z"
  132 + fill="#fff"
  133 + p-id="79419"
  134 + />
  135 + </svg>
  136 + </Button>
  137 + </Col>
  138 + </Row>
  139 + </Input.Group>
  140 + </template>
  141 + </BasicForm>
  142 + </section>
  143 + </Modal>
89 144 <Spin
90 145 :spinning="loading"
91 146 tip="正在加载中..."
... ... @@ -96,4 +151,10 @@
96 151 </Spin>
97 152 </template>
98 153
99   -<style lang="less" scoped></style>
  154 +<style lang="less">
  155 + .share-page-token-modal {
  156 + .ant-modal-close {
  157 + display: none;
  158 + }
  159 + }
  160 +</style>
... ...
... ... @@ -213,11 +213,7 @@
213 213 >
214 214 <div class="text-gray-400 text-xs truncate">
215 215 <span class="mr-2">最近执行</span>
216   - <span>{{
217   - getLastExecuteTime.value > 0
218   - ? `${getLastExecuteTime.value}${getLastExecuteTime.unitName}前`
219   - : '刚刚'
220   - }}</span>
  216 + <span>{{ getRecord.lastExecuteStr }}</span>
221 217 </div>
222 218 </Tooltip>
223 219 </div>
... ...
1 1 import { useWebSocket } from '@vueuse/core';
2 2 import { Ref, unref } from 'vue';
3 3 import { DataBoardLayoutInfo } from '../types/type';
4   -import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
5 4 import { useGlobSetting } from '/@/hooks/setting';
6   -import { getAuthCache } from '/@/utils/auth';
7 5 import { isNullAndUnDef } from '/@/utils/is';
  6 +import { getJwtToken, getShareJwtToken } from '/@/utils/auth';
  7 +import { isShareMode } from '/@/views/sys/share/hook';
8 8
9 9 interface SocketMessage {
10 10 tsSubCmds: SocketMessageItem[];
... ... @@ -50,7 +50,7 @@ const generateMessage = (deviceId: string, cmdId: number, attr: string): SocketM
50 50 };
51 51
52 52 export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) {
53   - const token = getAuthCache(JWT_TOKEN_KEY);
  53 + const token = isShareMode() ? getShareJwtToken() : getJwtToken();
54 54
55 55 const cmdIdMapping = new Map<number, GroupMappingRecord[]>();
56 56
... ...
... ... @@ -21,6 +21,8 @@ export interface RequestOptions {
21 21 ignoreCancelToken?: boolean;
22 22 // Whether to send token in header
23 23 withToken?: boolean;
  24 + // Carry and share token 携带分享的访问令牌
  25 + withShareToken?: boolean;
24 26 }
25 27
26 28 export interface Result<T = any> {
... ...