feat(功能用例): 飞书扫码后端配置页面

This commit is contained in:
guoyuqi 2024-07-02 16:05:18 +08:00 committed by 刘瑞斌
parent 8f31970302
commit add903346c
7 changed files with 534 additions and 12 deletions

View File

@ -1,19 +1,34 @@
import MSR from '@/api/http/index'; import MSR from '@/api/http/index';
import { import {
GetDingTalkInfoUrl, GetDingTalkInfoUrl,
GetLarkInfoUrl,
GetLarkSuiteInfoUrl,
GetPlatformInfoUrl, GetPlatformInfoUrl,
GetWeComInfoUrl, GetWeComInfoUrl,
PostDingTalkEnableUrl, PostDingTalkEnableUrl,
PostDingTalkSaveUrl, PostDingTalkSaveUrl,
PostDingTalkValidateFalseUrl, PostDingTalkValidateFalseUrl,
PostLarkEnableUrl,
PostLarkSaveUrl,
PostLarkSuiteEnableUrl,
PostLarkSuiteSaveUrl,
PostLarkSuiteValidateFalseUrl,
PostLarkValidateFalseUrl,
PostValidateDingTalkUrl, PostValidateDingTalkUrl,
PostValidateLarkSuiteUrl,
PostValidateLarkUrl,
PostValidateWeComUrl, PostValidateWeComUrl,
PostWeComEnableUrl, PostWeComEnableUrl,
PostWeComSaveUrl, PostWeComSaveUrl,
PostWeComValidateFalseUrl, PostWeComValidateFalseUrl,
} from '@/api/requrls/setting/qrCode'; } from '@/api/requrls/setting/qrCode';
import { DingTalkInfo, EnableEditorRequest, PlatformSourceList, WeComInfo } from '@/models/setting/qrCode'; import { DingTalkInfo, EnableEditorRequest, LarkInfo, PlatformSourceList, WeComInfo } from '@/models/setting/qrCode';
// 获取所有平台配置基础信息
export function getPlatformSourceList() {
return MSR.get<PlatformSourceList>({ url: GetPlatformInfoUrl });
}
// 获取企业微信配置 // 获取企业微信配置
export function getWeComInfo() { export function getWeComInfo() {
@ -25,6 +40,16 @@ export function getDingInfo() {
return MSR.get<DingTalkInfo>({ url: GetDingTalkInfoUrl }); return MSR.get<DingTalkInfo>({ url: GetDingTalkInfoUrl });
} }
// 获取飞书配置
export function getLarkInfo() {
return MSR.get<LarkInfo>({ url: GetLarkInfoUrl });
}
// 获取国际飞书配置
export function getLarkSuiteInfo() {
return MSR.get<LarkInfo>({ url: GetLarkSuiteInfoUrl });
}
// 保存企业微信登陆配置 // 保存企业微信登陆配置
export function saveWeComConfig(data: WeComInfo) { export function saveWeComConfig(data: WeComInfo) {
return MSR.post({ url: PostWeComSaveUrl, data }); return MSR.post({ url: PostWeComSaveUrl, data });
@ -35,6 +60,16 @@ export function saveDingTalkConfig(data: DingTalkInfo) {
return MSR.post({ url: PostDingTalkSaveUrl, data }); return MSR.post({ url: PostDingTalkSaveUrl, data });
} }
// 保存飞书登陆配置
export function saveLarkConfig(data: LarkInfo) {
return MSR.post({ url: PostLarkSaveUrl, data });
}
// 保存国际飞书登陆配置
export function saveLarkSuiteConfig(data: LarkInfo) {
return MSR.post({ url: PostLarkSuiteSaveUrl, data });
}
// 校验企业微信外链接 // 校验企业微信外链接
export function validateWeComConfig(data: WeComInfo) { export function validateWeComConfig(data: WeComInfo) {
return MSR.post({ url: PostValidateWeComUrl, data }); return MSR.post({ url: PostValidateWeComUrl, data });
@ -45,6 +80,16 @@ export function validateDingTalkConfig(data: DingTalkInfo) {
return MSR.post({ url: PostValidateDingTalkUrl, data }); return MSR.post({ url: PostValidateDingTalkUrl, data });
} }
// 校验飞书外链接
export function validateLarkConfig(data: LarkInfo) {
return MSR.post({ url: PostValidateLarkUrl, data });
}
// 校验国际飞书外链接
export function validateLarkSuiteConfig(data: LarkInfo) {
return MSR.post({ url: PostValidateLarkSuiteUrl, data });
}
// 开启企业微信登陆 // 开启企业微信登陆
export function enableWeCom(data: EnableEditorRequest) { export function enableWeCom(data: EnableEditorRequest) {
return MSR.post({ url: PostWeComEnableUrl, data }); return MSR.post({ url: PostWeComEnableUrl, data });
@ -55,9 +100,14 @@ export function enableDingTalk(data: EnableEditorRequest) {
return MSR.post({ url: PostDingTalkEnableUrl, data }); return MSR.post({ url: PostDingTalkEnableUrl, data });
} }
// 获取所有平台配置基础信息 // 开启飞书登陆
export function getPlatformSourceList() { export function enableLark(data: EnableEditorRequest) {
return MSR.get<PlatformSourceList>({ url: GetPlatformInfoUrl }); return MSR.post({ url: PostLarkEnableUrl, data });
}
// 开启国际飞书登陆
export function enableLarkSuite(data: EnableEditorRequest) {
return MSR.post({ url: PostLarkSuiteEnableUrl, data });
} }
// 开启企业微信登陆 // 开启企业微信登陆
@ -69,3 +119,13 @@ export function closeValidateWeCom() {
export function closeValidateDingTalk() { export function closeValidateDingTalk() {
return MSR.post({ url: PostDingTalkValidateFalseUrl }); return MSR.post({ url: PostDingTalkValidateFalseUrl });
} }
// 开启飞书登陆
export function closeValidateLark() {
return MSR.post({ url: PostLarkValidateFalseUrl });
}
// 开启国际飞书登陆
export function closeValidateLarkSuite() {
return MSR.post({ url: PostLarkSuiteValidateFalseUrl });
}

View File

@ -1,11 +1,24 @@
export const GetPlatformInfoUrl = '/setting/get/platform/info';
export const PostValidateWeComUrl = '/we_com/validate'; export const PostValidateWeComUrl = '/we_com/validate';
export const GetWeComInfoUrl = '/we_com/info/with_detail'; export const GetWeComInfoUrl = '/we_com/info/with_detail';
export const PostWeComSaveUrl = '/we_com/save'; export const PostWeComSaveUrl = '/we_com/save';
export const PostWeComEnableUrl = '/we_com/enable'; export const PostWeComEnableUrl = '/we_com/enable';
export const PostWeComValidateFalseUrl = '/we_com/change/validate';
export const PostValidateDingTalkUrl = '/ding_talk/validate'; export const PostValidateDingTalkUrl = '/ding_talk/validate';
export const GetDingTalkInfoUrl = '/ding_talk/info/with_detail'; export const GetDingTalkInfoUrl = '/ding_talk/info/with_detail';
export const PostDingTalkSaveUrl = '/ding_talk/save'; export const PostDingTalkSaveUrl = '/ding_talk/save';
export const PostDingTalkEnableUrl = '/ding_talk/enable'; export const PostDingTalkEnableUrl = '/ding_talk/enable';
export const GetPlatformInfoUrl = '/setting/get/platform/info';
export const PostWeComValidateFalseUrl = '/we_com/change/validate';
export const PostDingTalkValidateFalseUrl = '/ding_talk/change/validate'; export const PostDingTalkValidateFalseUrl = '/ding_talk/change/validate';
export const PostValidateLarkUrl = '/lark/validate';
export const GetLarkInfoUrl = '/lark/info/with_detail';
export const PostLarkSaveUrl = '/lark/save';
export const PostLarkEnableUrl = '/lark/enable';
export const PostLarkValidateFalseUrl = '/lark/change/validate';
export const PostValidateLarkSuiteUrl = '/lark_suite/validate';
export const GetLarkSuiteInfoUrl = '/lark_suite/info/with_detail';
export const PostLarkSuiteSaveUrl = '/lark_suite/save';
export const PostLarkSuiteEnableUrl = '/lark_suite/enable';
export const PostLarkSuiteValidateFalseUrl = '/lark_suite/change/validate';

View File

@ -7,6 +7,14 @@ export interface DingTalkInfo {
valid: boolean; valid: boolean;
} }
export interface LarkInfo {
agentId: string;
appSecret: string;
callBack: string;
enable: boolean;
valid: boolean;
}
export interface WeComInfo { export interface WeComInfo {
corpId: string; corpId: string;
agentId: string; agentId: string;

View File

@ -61,12 +61,7 @@
<a-button <a-button
type="outline" type="outline"
class="ml-[14px]" class="ml-[14px]"
:disabled=" :disabled="dingTalkForm.appKey == '' && dingTalkForm.appSecret == '' && dingTalkForm.agentId == ''"
dingTalkForm.appKey == '' &&
dingTalkForm.appSecret == '' &&
dingTalkForm.agentId == '' &&
dingTalkForm.callBack == ''
"
@click="validateInfo" @click="validateInfo"
> >
{{ t('organization.service.testLink') }} {{ t('organization.service.testLink') }}

View File

@ -0,0 +1,190 @@
<template>
<a-modal
v-model:visible="detailVisible"
title-align="start"
class="ms-modal-upload ms-modal-medium"
:width="680"
:loading="loading"
>
<template #title>
{{ t('project.messageManagement.LARK') }}
</template>
<a-form ref="larkFormRef" class="ms-form rounded-[4px]" :model="larkForm" layout="vertical">
<a-form-item
field="agentId"
:label="t('system.config.qrCodeConfig.agentId')"
:rules="[{ required: true, message: t('system.config.qrCodeConfig.agentId.required') }]"
:validate-trigger="['blur', 'input']"
asterisk-position="end"
>
<a-input v-model="larkForm.agentId" :max-length="255" :placeholder="t('formCreate.PleaseEnter')" />
</a-form-item>
<a-form-item
field="appSecret"
:label="t('system.config.qrCodeConfig.appSecret')"
:rules="[{ required: true, message: t('system.config.qrCodeConfig.appSecret.required') }]"
:validate-trigger="['blur', 'input']"
asterisk-position="end"
>
<a-input-password
v-model="larkForm.appSecret"
allow-clear
:max-length="255"
:placeholder="t('formCreate.PleaseEnter')"
/>
</a-form-item>
</a-form>
<template #footer>
<div class="footer-button">
<div class="ms-switch">
<a-switch
v-model="larkForm.enable"
class="ms-form-table-input-switch execute-form-table-input-switch"
size="small"
/>
<span class="ml-3 font-normal text-[var(--color-text-1)]">{{ t('system.config.qrCodeConfig.enable') }}</span>
</div>
<div class="ms-button-group">
<a-button type="secondary" class="ml-[14px]" @click="cancelEdit">
{{ t('common.cancel') }}
</a-button>
<a-button
type="outline"
class="ml-[14px]"
:disabled="larkForm.appSecret == '' && larkForm.agentId == ''"
@click="validateInfo"
>
{{ t('organization.service.testLink') }}
</a-button>
<a-button
:disabled="larkForm.appSecret == '' && larkForm.agentId == ''"
type="primary"
class="ml-[14px]"
@click="saveInfo"
>
{{ t('common.confirm') }}
</a-button>
</div>
</div>
</template>
</a-modal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { FormInstance, ValidatedError } from '@arco-design/web-vue';
import { getLarkInfo, saveLarkConfig, validateLarkConfig } from '@/api/modules/setting/qrCode';
import { useI18n } from '@/hooks/useI18n';
import { LarkInfo } from '@/models/setting/qrCode';
import Message from '@arco-design/web-vue/es/message';
const { t } = useI18n();
const larkForm = ref<LarkInfo>({
agentId: '',
appSecret: '',
callBack: '',
enable: false,
valid: false,
});
const larkFormRef = ref<FormInstance | null>(null);
const loading = ref<boolean>(false);
const detailVisible = ref<boolean>(false);
const props = defineProps<{
visible: boolean;
}>();
const emits = defineEmits<{
(event: 'update:visible', visible: boolean): void;
(event: 'success'): void;
}>();
//
const loadList = async () => {
loading.value = true;
try {
larkForm.value = await getLarkInfo();
} catch (error) {
console.log(error);
} finally {
loading.value = false;
}
};
watchEffect(() => {
detailVisible.value = props.visible;
});
watch(
() => detailVisible.value,
(val) => {
emits('update:visible', val);
loadList();
}
);
function cancelEdit() {
detailVisible.value = false;
emits('update:visible', detailVisible.value);
}
async function validateInfo() {
larkFormRef.value?.validate(async (errors: Record<string, ValidatedError> | undefined) => {
if (!errors) {
loading.value = true;
try {
await validateLarkConfig(larkForm.value);
larkForm.value.valid = true;
Message.success(t('organization.service.testLinkStatusTip'));
} catch (error) {
larkForm.value.valid = false;
console.log(error);
} finally {
loading.value = false;
}
}
});
}
async function saveInfo() {
larkFormRef.value?.validate(async (errors: Record<string, ValidatedError> | undefined) => {
if (!errors) {
loading.value = true;
try {
await saveLarkConfig(larkForm.value);
Message.success(t('common.saveSuccess'));
emits('success');
} catch (error) {
console.log(error);
} finally {
loading.value = false;
detailVisible.value = false;
}
}
});
}
</script>
<style scoped lang="less">
.footer-button {
display: flex;
align-items: center;
flex-direction: row;
justify-content: space-between;
}
.ms-switch {
display: flex;
align-items: center;
flex-direction: row;
}
.ms-button-group {
display: flex;
align-items: center;
flex-direction: row;
}
</style>

View File

@ -0,0 +1,190 @@
<template>
<a-modal
v-model:visible="detailVisible"
title-align="start"
class="ms-modal-upload ms-modal-medium"
:width="680"
:loading="loading"
>
<template #title>
{{ t('project.messageManagement.LARK_SUITE') }}
</template>
<a-form ref="larkSuiteFormRef" class="ms-form rounded-[4px]" :model="larkSuiteForm" layout="vertical">
<a-form-item
field="agentId"
:label="t('system.config.qrCodeConfig.agentId')"
:rules="[{ required: true, message: t('system.config.qrCodeConfig.agentId.required') }]"
:validate-trigger="['blur', 'input']"
asterisk-position="end"
>
<a-input v-model="larkSuiteForm.agentId" :max-length="255" :placeholder="t('formCreate.PleaseEnter')" />
</a-form-item>
<a-form-item
field="appSecret"
:label="t('system.config.qrCodeConfig.appSecret')"
:rules="[{ required: true, message: t('system.config.qrCodeConfig.appSecret.required') }]"
:validate-trigger="['blur', 'input']"
asterisk-position="end"
>
<a-input-password
v-model="larkSuiteForm.appSecret"
allow-clear
:max-length="255"
:placeholder="t('formCreate.PleaseEnter')"
/>
</a-form-item>
</a-form>
<template #footer>
<div class="footer-button">
<div class="ms-switch">
<a-switch
v-model="larkSuiteForm.enable"
class="ms-form-table-input-switch execute-form-table-input-switch"
size="small"
/>
<span class="ml-3 font-normal text-[var(--color-text-1)]">{{ t('system.config.qrCodeConfig.enable') }}</span>
</div>
<div class="ms-button-group">
<a-button type="secondary" class="ml-[14px]" @click="cancelEdit">
{{ t('common.cancel') }}
</a-button>
<a-button
type="outline"
class="ml-[14px]"
:disabled="larkSuiteForm.appSecret == '' && larkSuiteForm.agentId == ''"
@click="validateInfo"
>
{{ t('organization.service.testLink') }}
</a-button>
<a-button
type="primary"
:disabled="larkSuiteForm.appSecret == '' && larkSuiteForm.agentId == ''"
class="ml-[14px]"
@click="saveInfo"
>
{{ t('common.confirm') }}
</a-button>
</div>
</div>
</template>
</a-modal>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { FormInstance, ValidatedError } from '@arco-design/web-vue';
import { getLarkSuiteInfo, saveLarkSuiteConfig, validateLarkSuiteConfig } from '@/api/modules/setting/qrCode';
import { useI18n } from '@/hooks/useI18n';
import { LarkInfo } from '@/models/setting/qrCode';
import Message from '@arco-design/web-vue/es/message';
const { t } = useI18n();
const larkSuiteForm = ref<LarkInfo>({
agentId: '',
appSecret: '',
callBack: '',
enable: false,
valid: false,
});
const larkSuiteFormRef = ref<FormInstance | null>(null);
const loading = ref<boolean>(false);
const detailVisible = ref<boolean>(false);
const props = defineProps<{
visible: boolean;
}>();
const emits = defineEmits<{
(event: 'update:visible', visible: boolean): void;
(event: 'success'): void;
}>();
//
const loadList = async () => {
loading.value = true;
try {
larkSuiteForm.value = await getLarkSuiteInfo();
} catch (error) {
console.log(error);
} finally {
loading.value = false;
}
};
watchEffect(() => {
detailVisible.value = props.visible;
});
watch(
() => detailVisible.value,
(val) => {
emits('update:visible', val);
loadList();
}
);
function cancelEdit() {
detailVisible.value = false;
emits('update:visible', detailVisible.value);
}
async function validateInfo() {
larkSuiteFormRef.value?.validate(async (errors: Record<string, ValidatedError> | undefined) => {
if (!errors) {
loading.value = true;
try {
await validateLarkSuiteConfig(larkSuiteForm.value);
larkSuiteForm.value.valid = true;
Message.success(t('organization.service.testLinkStatusTip'));
} catch (error) {
larkSuiteForm.value.valid = false;
console.log(error);
} finally {
loading.value = false;
}
}
});
}
async function saveInfo() {
larkSuiteFormRef.value?.validate(async (errors: Record<string, ValidatedError> | undefined) => {
if (!errors) {
loading.value = true;
try {
await saveLarkSuiteConfig(larkSuiteForm.value);
Message.success(t('common.saveSuccess'));
emits('success');
} catch (error) {
console.log(error);
} finally {
loading.value = false;
detailVisible.value = false;
}
}
});
}
</script>
<style scoped lang="less">
.footer-button {
display: flex;
align-items: center;
flex-direction: row;
justify-content: space-between;
}
.ms-switch {
display: flex;
align-items: center;
flex-direction: row;
}
.ms-button-group {
display: flex;
align-items: center;
flex-direction: row;
}
</style>

View File

@ -105,6 +105,8 @@
</MsCard> </MsCard>
<we-com-modal v-model:visible="showWeComModal" @success="loadList()" /> <we-com-modal v-model:visible="showWeComModal" @success="loadList()" />
<ding-talk-modal v-model:visible="showDingTalkModal" @success="loadList()" /> <ding-talk-modal v-model:visible="showDingTalkModal" @success="loadList()" />
<lark-modal v-model:visible="showLarkModal" @success="loadList()" />
<lark-suite-modal v-model:visible="showLarkSuiteModal" @success="loadList()" />
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@ -113,15 +115,23 @@
import MsCard from '@/components/pure/ms-card/index.vue'; import MsCard from '@/components/pure/ms-card/index.vue';
import MsIcon from '@/components/pure/ms-icon-font/index.vue'; import MsIcon from '@/components/pure/ms-icon-font/index.vue';
import DingTalkModal from '@/views/setting/system/config/components/dingTalkModal.vue'; import DingTalkModal from '@/views/setting/system/config/components/dingTalkModal.vue';
import LarkModal from '@/views/setting/system/config/components/larkModal.vue';
import LarkSuiteModal from '@/views/setting/system/config/components/larkSuiteModal.vue';
import WeComModal from '@/views/setting/system/config/components/weComModal.vue'; import WeComModal from '@/views/setting/system/config/components/weComModal.vue';
import { import {
closeValidateDingTalk, closeValidateDingTalk,
closeValidateLark,
closeValidateLarkSuite,
closeValidateWeCom, closeValidateWeCom,
enableDingTalk, enableDingTalk,
enableLark,
enableLarkSuite,
enableWeCom, enableWeCom,
getPlatformSourceList, getPlatformSourceList,
validateDingTalkConfig, validateDingTalkConfig,
validateLarkConfig,
validateLarkSuiteConfig,
validateWeComConfig, validateWeComConfig,
} from '@/api/modules/setting/qrCode'; } from '@/api/modules/setting/qrCode';
import { useI18n } from '@/hooks/useI18n'; import { useI18n } from '@/hooks/useI18n';
@ -130,6 +140,7 @@
import { import {
DingTalkInfo, DingTalkInfo,
EnableEditorRequest, EnableEditorRequest,
LarkInfo,
PlatformConfigItem, PlatformConfigItem,
PlatformConfigList, PlatformConfigList,
PlatformSource, PlatformSource,
@ -160,11 +171,31 @@
logo: 'icon-logo_dingtalk', logo: 'icon-logo_dingtalk',
edit: false, edit: false,
}, },
{
key: 'LARK',
title: t('project.messageManagement.LARK'),
description: '先进企业合作与管理平台',
enable: false,
valid: false,
logo: 'icon-logo_dingtalk',
edit: false,
},
{
key: 'LARK_SUITE',
title: t('project.messageManagement.LARK_SUITE'),
description: '先进企业合作与管理平台',
enable: false,
valid: false,
logo: 'icon-logo_dingtalk',
edit: false,
},
]); ]);
const data = ref<PlatformSourceList>([]); const data = ref<PlatformSourceList>([]);
const loading = ref<boolean>(false); const loading = ref<boolean>(false);
const showWeComModal = ref<boolean>(false); const showWeComModal = ref<boolean>(false);
const showDingTalkModal = ref<boolean>(false); const showDingTalkModal = ref<boolean>(false);
const showLarkModal = ref<boolean>(false);
const showLarkSuiteModal = ref<boolean>(false);
const weComInfo = ref<WeComInfo>({ const weComInfo = ref<WeComInfo>({
corpId: '', corpId: '',
@ -184,6 +215,14 @@
valid: false, valid: false,
}); });
const larkInfo = ref<LarkInfo>({
agentId: '',
appSecret: '',
callBack: '',
enable: false,
valid: false,
});
// //
const loadList = async () => { const loadList = async () => {
loading.value = true; loading.value = true;
@ -210,9 +249,23 @@
if (key === 'WE_COM') { if (key === 'WE_COM') {
showWeComModal.value = true; showWeComModal.value = true;
showDingTalkModal.value = false; showDingTalkModal.value = false;
showLarkModal.value = false;
showLarkSuiteModal.value = false;
} else if (key === 'DING_TALK') { } else if (key === 'DING_TALK') {
showWeComModal.value = false; showWeComModal.value = false;
showDingTalkModal.value = true; showDingTalkModal.value = true;
showLarkModal.value = false;
showLarkSuiteModal.value = false;
} else if (key === 'LARK') {
showWeComModal.value = false;
showDingTalkModal.value = false;
showLarkModal.value = true;
showLarkSuiteModal.value = false;
} else if (key === 'LARK_SUITE') {
showWeComModal.value = false;
showDingTalkModal.value = false;
showLarkModal.value = false;
showLarkSuiteModal.value = true;
} }
}; };
@ -224,6 +277,10 @@
await validateWeComConfig(weComInfo.value); await validateWeComConfig(weComInfo.value);
} else if (key === 'DING_TALK') { } else if (key === 'DING_TALK') {
await validateDingTalkConfig(dingTalkInfo.value); await validateDingTalkConfig(dingTalkInfo.value);
} else if (key === 'LARK') {
await validateLarkConfig(larkInfo.value);
} else if (key === 'LARK_SUITE') {
await validateLarkSuiteConfig(larkInfo.value);
} }
Message.success(t('organization.service.testLinkStatusTip')); Message.success(t('organization.service.testLinkStatusTip'));
} catch (error) { } catch (error) {
@ -231,7 +288,12 @@
await closeValidateWeCom(); await closeValidateWeCom();
} else if (key === 'DING_TALK') { } else if (key === 'DING_TALK') {
await closeValidateDingTalk(); await closeValidateDingTalk();
} else if (key === 'LARK') {
await closeValidateLark();
} else if (key === 'LARK_SUITE') {
await closeValidateLarkSuite();
} }
console.log(error); console.log(error);
} finally { } finally {
loadList(); loadList();
@ -251,6 +313,10 @@
await enableWeCom(params); await enableWeCom(params);
} else if (key === 'DING_TALK') { } else if (key === 'DING_TALK') {
await enableDingTalk(params); await enableDingTalk(params);
} else if (key === 'LARK') {
await enableLark(params);
} else if (key === 'LARK_SUITE') {
await enableLarkSuite(params);
} }
Message.success(t(message)); Message.success(t(message));
loadList(); loadList();