feat: Add Wecom Configuration Form

This commit is contained in:
fit2cloud-chenyw
2026-01-07 16:07:15 +08:00
committed by fit2cloud-chenyw
parent d56e4779ff
commit 01ca5ec125
15 changed files with 2452 additions and 1 deletions
+7
View File
@@ -756,6 +756,13 @@
"setting_successfully": "Setting Successfully",
"customize_theme_color": "Customize theme color"
},
"platform": {
"title": "Platform Integration",
"status_open": "Enabled",
"status_close": "Disabled",
"access_in": "Access",
"can_enable_it": "Can be enabled after successful test connection"
},
"authentication": {
"invalid": "Invalid",
"valid": "Valid",
+7
View File
@@ -756,6 +756,13 @@
"setting_successfully": "설정 성공",
"customize_theme_color": "사용자 정의 테마 색상"
},
"platform": {
"title": "플랫폼 연동",
"status_open": "활성화됨",
"status_close": "비활성화됨",
"access_in": "접속",
"can_enable_it": "연결 테스트 성공 후 활성화 가능"
},
"authentication": {
"invalid": "유효하지 않음",
"valid": "유효함",
+7 -1
View File
@@ -215,7 +215,6 @@
"chart_selected": "已选{0}"
},
"qa": {
"recommended_repetitive_tips": "Duplicate questions exist",
"retrieve_error": "暂无内容",
"retrieve_again": "重新获取",
"recently": "最近",
@@ -757,6 +756,13 @@
"setting_successfully": "设置成功",
"customize_theme_color": "自定义主题色"
},
"platform": {
"title": "平台对接",
"status_open": "已开启",
"status_close": "已关闭",
"access_in": "接入",
"can_enable_it": "测试连接有效后,可开启"
},
"authentication": {
"invalid": "无效",
"valid": "有效",
+7
View File
@@ -21,6 +21,7 @@ import Audit from '@/views/system/audit/index.vue'
import Appearance from '@/views/system/appearance/index.vue'
import Parameter from '@/views/system/parameter/index.vue'
import Authentication from '@/views/system/authentication/index.vue'
import Platform from '@/views/system/platform/index.vue'
import Permission from '@/views/system/permission/index.vue'
import User from '@/views/system/user/User.vue'
import Workspace from '@/views/system/workspace/index.vue'
@@ -212,6 +213,12 @@ export const routes = [
component: Authentication,
meta: { title: t('system.authentication_settings') },
},
{
path: 'platform',
name: 'platform',
component: Platform,
meta: { title: t('platform.title') },
},
],
},
{
@@ -0,0 +1,318 @@
<template>
<div class="info-template-container">
<div v-if="!props.hideHead" class="info-template-header">
<div class="info-template-title">
<span>{{ curTitle }}</span>
</div>
<div>
<el-button v-if="testConnectText" secondary @click="check">{{ testConnectText }}</el-button>
<el-button v-if="showValidate" secondary @click="check">{{
t('datasource.validate')
}}</el-button>
<el-button type="primary" @click="edit">{{ t('commons.edit') }}</el-button>
</div>
</div>
<div class="info-template-content clearfix">
<div v-for="item in settingList" :key="item.pkey" class="info-content-item">
<div class="info-item-label">
<!-- <span>{{ t(item.pkey) }}</span> -->
<span>{{ item.pkey }}</span>
<el-tooltip
v-if="tooltipItem[item.pkey]"
effect="dark"
:content="tooltipItem[item.pkey]"
placement="top"
>
<el-icon class="info-tips"
><Icon name="dv-info"><dvInfo class="svg-icon" /></Icon
></el-icon>
</el-tooltip>
</div>
<div class="info-item-content">
<div v-if="item.type === 'pwd'" class="info-item-pwd">
<span class="info-item-pwd-span">{{
pwdItem[item.pkey]['hidden'] ? '********' : item.pval
}}</span>
<el-tooltip
v-if="props.copyList.includes(item.pkey)"
effect="dark"
:content="t('datasource.copy')"
placement="top"
>
<el-button text class="setting-tip-btn" @click="copyVal(item.pval)">
<template #icon>
<Icon name="de-copy"><icon_copy_outlined class="svg-icon" /></Icon>
</template>
</el-button>
</el-tooltip>
<el-tooltip
effect="dark"
:content="
pwdItem[item.pkey]['hidden']
? t('embedded.click_to_show')
: t('embedded.click_to_hide')
"
placement="top"
>
<el-button text class="setting-tip-btn" @click="switchPwd(item.pkey)">
<template #icon>
<Icon
><component
:is="
pwdItem[item.pkey]['hidden']
? icon_invisible_outlined
: icon_visible_outlined
"
class="svg-icon"
></component
></Icon>
</template>
</el-button>
</el-tooltip>
</div>
<!-- <span v-else-if="item.pkey.includes('basic.dsIntervalTime')">
<span>{{ item.pval + ' ' + executeTime + t('common.every_exec') }}</span>
</span> -->
<template v-else>
<div class="info-item-content-val">
<span style="word-break: break-all">{{ item.pval }}</span>
<el-tooltip
v-if="props.copyList.includes(item.pkey)"
effect="dark"
:content="t('datasource.copy')"
placement="top"
>
<el-icon class="info-tips hover-icon_with_bg" @click="copyVal(item.pval)">
<icon_copy_outlined class="svg-icon" />
</el-icon>
</el-tooltip>
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import icon_copy_outlined from '@/assets/svg/icon_copy_outlined.svg'
import icon_invisible_outlined from '@/assets/embedded/icon_invisible_outlined.svg'
import icon_visible_outlined from '@/assets/embedded/icon_visible_outlined.svg'
import dvInfo from '@/assets/svg/dashboard-info.svg'
import { ref, computed, type PropType } from 'vue'
import { useI18n } from 'vue-i18n'
import { useClipboard } from '@vueuse/core'
import { ElMessage } from 'element-plus-secondary'
import type { SettingRecord, ToolTipRecord } from './SettingTemplate'
const { copy } = useClipboard({ legacy: true })
const { t } = useI18n()
const props = defineProps({
settingKey: {
type: String,
default: 'basic',
},
labelTooltips: {
type: Array as PropType<ToolTipRecord[]>,
default: () => [],
},
settingData: {
type: Array as PropType<SettingRecord[]>,
default: () => [],
},
settingTitle: {
type: String,
default: '',
},
hideHead: {
type: Boolean,
default: false,
},
showValidate: {
type: Boolean,
default: false,
},
testConnectText: {
type: String,
default: null,
},
copyList: {
type: Array as PropType<string[]>,
default: () => [],
},
})
// const executeTime = ref(t('system.and_0_seconds'))
const curTitle = computed(() => {
return props.settingTitle || t('system.basic_settings')
})
const copyVal = async (val: any) => {
try {
await copy(val)
ElMessage.success(t('embedded.copy_successful'))
} catch (e) {
console.error(e)
ElMessage.warning(t('embedded.copy_failed'))
}
}
const loadList = () => {
settingList.value = []
if (props.settingData?.length) {
props.settingData.forEach((item) => {
/* if (item.pkey.includes('basic.dsExecuteTime')) {
executeTime.value = getExecuteTime(item.pval)
} else {
settingList.value.push(item)
} */
settingList.value.push(item)
})
}
}
/* const getExecuteTime = (val: any) => {
const options = [
{ value: 'minute', label: t('system.time_0_seconds') },
{ value: 'hour', label: t('system.and_0_seconds_de') },
]
return options.filter((item) => item.value === val)[0].label
} */
const settingList = ref([] as SettingRecord[])
const init = () => {
if (props.settingData?.length) {
loadList()
}
}
const pwdItem = ref({} as Record<string, { hidden: boolean }>)
const formatPwd = () => {
settingList.value.forEach((setting) => {
if (setting.type === 'pwd') {
pwdItem.value[setting.pkey] = { hidden: true }
}
})
}
const tooltipItem = ref({} as Record<string, any>)
const formatLabel = () => {
if (props.labelTooltips?.length) {
props.labelTooltips.forEach((tooltip) => {
tooltipItem.value[tooltip.key] = tooltip.val
})
}
}
const switchPwd = (pkey: string) => {
pwdItem.value[pkey]['hidden'] = !pwdItem.value[pkey]['hidden']
}
const emits = defineEmits(['edit', 'check'])
const edit = () => {
emits('edit')
}
const check = () => {
emits('check')
}
defineExpose({
init,
})
init()
formatPwd()
formatLabel()
</script>
<style lang="less" scope>
.setting-tip-btn {
height: 24px !important;
width: 24px !important;
margin-left: 4px !important;
.ed-icon {
font-size: 16px;
}
}
.info-template-container {
padding: 24px 24px 8px 24px;
background: var(--ContentBG, #ffffff);
border-radius: 4px;
.info-template-header {
display: flex;
margin-top: -4px;
align-items: center;
justify-content: space-between;
.info-template-title {
height: 24px;
line-height: 23px;
font-size: 16px;
font-weight: 500;
color: #1f2329;
}
}
.info-template-content {
width: 100%;
margin-top: 12px;
.info-content-item {
width: 50%;
float: left;
margin-bottom: 16px;
min-height: 46px;
.info-item-label {
height: 22px;
line-height: 22px;
display: flex;
align-items: center;
span {
font-size: 14px;
color: #646a73;
font-weight: 400;
}
i {
margin-left: 2px;
}
}
.info-item-content {
line-height: 22px;
span {
font-size: 14px;
color: #1f2329;
font-weight: 400;
}
.info-item-pwd {
height: 22px;
line-height: 22px;
width: 100%;
display: flex;
align-items: center;
i {
margin-left: 2px;
}
.info-item-pwd-span {
max-width: calc(100% - 84px);
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.info-item-content-val {
display: flex;
align-items: center;
column-gap: 6px;
.hover-icon_with_bg {
color: var(--ed-color-primary);
background-color: var(--ed-button-hover-bg-color);
/* &::after {
background: var(--ed-button-hover-bg-color);
} */
}
}
}
}
}
.clearfix::after {
content: '';
display: table;
clear: both;
}
}
</style>
@@ -0,0 +1,11 @@
export interface SettingRecord {
pkey: string
pval: string
type: string
sort: number
}
export interface ToolTipRecord {
key: string
val: string
}
@@ -0,0 +1,280 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ElMessage, ElLoading } from 'element-plus-secondary'
import { request } from '@/utils/request'
import { useI18n } from 'vue-i18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
const { t } = useI18n()
const dialogVisible = ref(false)
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
const dingtalkForm = ref<FormInstance>()
interface DingtalkForm {
agentId?: string
appKey?: string
appSecret?: string
callBack?: string
enable?: string
valid?: string
}
const state = reactive({
form: reactive<DingtalkForm>({
agentId: '',
appKey: '',
appSecret: '',
callBack: '',
enable: 'false',
valid: 'false',
}),
})
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const validateUrl = (rule, value, callback) => {
const reg = new RegExp(/(http|https):\/\/([\w.]+\/?)\S*/)
if (!reg.test(value)) {
callback(new Error(t('system.incorrect_please_re_enter')))
} else {
callback()
}
}
const rule = reactive<FormRules>({
agentId: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 20,
message: t('common.input_limit', [5, 20]),
trigger: 'blur',
},
],
appKey: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 20,
message: t('common.input_limit', [5, 20]),
trigger: 'blur',
},
],
appSecret: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 100,
message: t('common.input_limit', [5, 100]),
trigger: 'blur',
},
],
callBack: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 10,
max: 100,
message: t('common.input_limit', [10, 100]),
trigger: 'blur',
},
{ required: true, validator: validateUrl, trigger: 'blur' },
],
enable: [
{
required: true,
message: t('common.require'),
trigger: 'change',
},
],
valid: [
{
required: true,
message: t('common.require'),
trigger: 'change',
},
],
})
const edit = (row: any) => {
state.form = {
agentId: row.agentId,
appKey: row.appKey,
appSecret: row.appSecret,
callBack: row.callBack,
enable: row.enable,
valid: row.valid,
}
dialogVisible.value = true
}
const emits = defineEmits(['saved'])
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {
if (valid) {
const param = { ...state.form }
const method = request.post('/dingtalk/create', param)
showLoading()
method
.then((res) => {
if (!res.msg) {
ElMessage.success(t('common.save_success'))
emits('saved')
reset()
}
closeLoading()
})
.catch(() => {
closeLoading()
})
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dialogVisible.value = false
}
const reset = () => {
resetForm(dingtalkForm.value)
}
const showLoading = () => {
loadingInstance.value = ElLoading.service({
target: '.platform-info-drawer',
})
}
const closeLoading = () => {
loadingInstance.value?.close()
}
const validate = () => {
const url = '/dingtalk/validate'
const data = state.form
showLoading()
request
.post(url, data)
.then(() => {
state.form.valid = 'true'
ElMessage.success(t('datasource.validate_success'))
})
.catch(() => {
state.form.enable = 'false'
state.form.valid = 'false'
})
.finally(() => {
closeLoading()
emits('saved')
})
}
defineExpose({
edit,
})
</script>
<template>
<el-drawer
v-model="dialogVisible"
:title="t('system.dingtalk_settings')"
modal-class="platform-info-drawer"
size="600px"
direction="rtl"
>
<el-form
ref="dingtalkForm"
require-asterisk-position="right"
:model="state.form"
:rules="rule"
label-width="80px"
label-position="top"
>
<el-form-item label="AgentId" prop="agentId">
<el-input v-model="state.form.agentId" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="APP Key" prop="appKey">
<el-input v-model="state.form.appKey" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="APP Secret" prop="appSecret">
<el-input
v-model="state.form.appSecret"
type="password"
show-password
:placeholder="t('common.please_input')"
/>
</el-form-item>
<el-form-item :label="t('system.callback_domain_name')" prop="callBack">
<el-input v-model="state.form.callBack" :placeholder="t('common.please_input')" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="resetForm(dingtalkForm)">{{ t('common.cancel') }}</el-button>
<el-button
:disabled="!state.form.appKey || !state.form.appSecret || !state.form.callBack"
@click="validate"
>
{{ t('commons.validate') }}
</el-button>
<el-button type="primary" @click="submitForm(dingtalkForm)">
{{ t('commons.save') }}
</el-button>
</span>
</template>
</el-drawer>
</template>
<style lang="less">
.platform-info-drawer {
.ed-drawer__footer {
height: 64px !important;
padding: 16px 24px !important;
.dialog-footer {
height: 32px;
line-height: 32px;
}
}
.ed-form-item__label {
line-height: 22px !important;
height: 22px !important;
}
}
</style>
<style lang="less" scoped>
.platform-info-drawer {
.ed-form-item {
margin-bottom: 16px;
}
.is-error {
margin-bottom: 40px !important;
}
.input-with-select {
.ed-input-group__prepend {
width: 72px;
background-color: #fff;
padding: 0 20px;
color: #1f2329;
text-align: center;
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
}
}
</style>
@@ -0,0 +1,246 @@
<template>
<div class="platform-head-container" :class="{ 'just-head': !existInfo }">
<div class="platform-setting-head">
<div class="platform-setting-head-left">
<div class="lead-left-icon">
<el-icon>
<Icon name="logo_dingtalk"><logo_dingtalk class="svg-icon" /></Icon>
</el-icon>
<span>{{ t('user.dingtalk') }}</span>
</div>
<div v-if="existInfo" class="lead-left-status" :class="{ invalid: !info.valid }">
<span>{{
info.valid ? t('datasource.valid') : info.appKey ? t('datasource.invalid') : ''
}}</span>
</div>
</div>
<div v-if="existInfo" class="platform-setting-head-right">
<span>{{ info.enable ? t('system.enabled') : t('system.close') }}</span>
<el-switch
v-if="info.valid"
v-model="info.enable"
class="status-switch"
@change="switchEnableApi"
/>
<el-tooltip
v-else
class="box-item"
effect="dark"
:content="t('platform.can_enable_it')"
placement="top"
>
<el-switch
v-model="info.enable"
disabled=""
class="status-switch"
@change="switchEnableApi"
/>
</el-tooltip>
</div>
<div v-else class="platform-setting-head-right-btn">
<el-button type="primary" @click="edit">{{ t('system.access') }}</el-button>
</div>
</div>
</div>
<InfoTemplate
v-if="existInfo"
class="platform-setting-main"
:copy-list="copyList"
setting-key="dingtalk"
setting-title=""
:hide-head="true"
:setting-data="settingList"
@edit="edit"
/>
<div v-if="existInfo" class="platform-foot-container">
<el-button type="primary" @click="edit">
{{ t('commons.edit') }}
</el-button>
<el-button
secondary
:disabled="!info.appKey || !info.appSecret || !info.callBack"
@click="validate"
>{{ t('commons.validate') }}</el-button
>
</div>
<dingtalk-edit ref="editor" @saved="search" />
</template>
<script lang="ts" setup>
import logo_dingtalk from '@/assets/svg/logo_dingtalk.svg'
import { ref, reactive } from 'vue'
import InfoTemplate from '../common/InfoTemplate.vue'
import DingtalkEdit from './DingtalkEdit.vue'
import { request } from '@/utils/request'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus-secondary'
const existInfo = ref(false)
const { t } = useI18n()
const editor = ref()
const copyList = ['APP Key', 'APP Secret']
const settingList = reactive([
{
pkey: 'AgentId',
pval: 'smtp.exmail.qq.com',
type: 'text',
sort: 2,
},
{
pkey: 'APP Key',
pval: 'smtp.exmail.qq.com',
type: 'text',
sort: 3,
},
{
pkey: 'APP Secret',
pval: 'smtp.exmail.qq.com',
type: 'pwd',
sort: 4,
},
{
pkey: t('system.callback_domain_name'),
pval: 'https://de.fit2cloud.com',
type: 'text',
sort: 5,
},
])
const info = ref({}) as any
const mappingArray = ['agentId', 'appKey', 'appSecret', 'callBack', 'enable']
const search = () => {
const url = '/dingtalk/info'
request.get(url).then((res) => {
info.value = res.data
existInfo.value = info.value['agentId']
for (let index = 0; index < settingList.length; index++) {
const element = settingList[index]
const key = mappingArray[index]
if (index === 4) {
element['pval'] = res.data[key] ? t('chart.open') : t('chart.close')
} else {
element['pval'] = res.data[key] || '-'
}
}
})
}
const switchEnableApi = (enable: any) => {
const url = '/dingtalk/switchEnable'
const data = { enable }
request.post(url, data).catch(() => {
info.value.enable = false
})
}
const edit = () => {
editor?.value.edit(info.value)
}
const validate = () => {
if (info.value?.appKey && info.value?.appSecret && info.value?.callBack) {
validateHandler()
}
}
const validateHandler = () => {
const url = '/dingtalk/validate'
const data = info.value
request
.post(url, data)
.then(() => {
info.value.valid = true
ElMessage.success(t('datasource.validate_success'))
})
.catch(() => {
info.value.enable = false
info.value.valid = false
})
}
search()
</script>
<style lang="less" scoped>
.platform-head-container {
height: 41px;
border-bottom: 1px solid #1f232926;
}
.just-head {
height: auto !important;
border: none !important;
}
.platform-setting-head {
height: 24px;
display: flex;
align-items: center;
justify-content: space-between;
.platform-setting-head-left {
display: flex;
.lead-left-icon {
display: flex;
line-height: 24px;
align-items: center;
i {
width: 24px;
height: 24px;
font-size: 20px;
}
span {
margin-left: 4px;
font-family: var(--de-custom_font, 'PingFang');
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 24px;
}
}
.lead-left-status {
margin-left: 4px;
height: 24px;
background: #34c72433;
padding: 0 6px;
font-size: 14px;
border-radius: 2px;
overflow: hidden;
span {
line-height: 24px;
color: #2ca91f;
}
}
.invalid {
background: #f54a4533 !important;
span {
color: #d03f3b !important;
}
}
}
.platform-setting-head-right-btn {
height: 32px;
line-height: 32px;
}
.platform-setting-head-right {
height: 22px;
line-height: 24px;
display: flex;
span {
margin-right: 8px;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.status-switch {
line-height: 22px !important;
height: 22px !important;
}
}
}
.platform-setting-main {
display: inline-block;
width: 100%;
padding: 16px 0 0 0 !important;
::v-deep(.info-template-content) {
display: contents !important;
}
}
.platform-foot-container {
height: 32px;
margin-top: -7px;
}
</style>
@@ -0,0 +1,59 @@
<template>
<p class="router-title">{{ t('platform.title') }}</p>
<div class="sys-setting-p">
<div class="container-sys-platform">
<wecom-info />
</div>
<!-- <div class="container-sys-platform not-first">
<dingtalk-info />
</div> -->
<!-- <div class="container-sys-platform not-first">
<lark-info />
</div>
<div class="container-sys-platform not-first">
<larksuite-info />
</div> -->
</div>
</template>
<script lang="ts" setup>
import WecomInfo from './wecom/WecomInfo.vue'
/* import DingtalkInfo from './dingtalk/DingtalkInfo.vue' */
/* import LarkInfo from './lark/LarkInfo.vue'
import LarksuiteInfo from './larksuite/LarksuiteInfo.vue' */
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
</script>
<style lang="less" scoped>
.router-title {
color: #1f2329;
font-feature-settings:
'clig' off,
'liga' off;
font-family: var(--de-custom_font, 'PingFang');
font-size: 20px;
font-style: normal;
font-weight: 500;
line-height: 28px;
}
.sys-setting-p {
width: 100%;
overflow-y: auto;
height: calc(100vh - 136px);
box-sizing: border-box;
margin-top: 16px;
}
.container-sys-platform {
padding: 24px;
overflow: hidden;
//border-radius: 4px;
background: var(--ContentBG, #ffffff);
border-radius: 12px;
border: 1px solid #dee0e3;
}
.not-first {
margin-top: 16px;
}
</style>
@@ -0,0 +1,262 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ElMessage, ElLoading } from 'element-plus-secondary'
import { request } from '@/utils/request'
import { useI18n } from 'vue-i18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
const { t } = useI18n()
const dialogVisible = ref(false)
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
const larkForm = ref<FormInstance>()
interface LarkForm {
appId?: string
appSecret?: string
callBack?: string
enable?: string
valid?: string
}
const state = reactive({
form: reactive<LarkForm>({
appId: '',
appSecret: '',
callBack: '',
enable: 'false',
valid: 'false',
}),
})
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const validateUrl = (rule, value, callback) => {
const reg = new RegExp(/(http|https):\/\/([\w.]+\/?)\S*/)
if (!reg.test(value)) {
callback(new Error(t('system.incorrect_please_re_enter')))
} else {
callback()
}
}
const rule = reactive<FormRules>({
appId: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 20,
message: t('common.input_limit', [5, 20]),
trigger: 'blur',
},
],
appSecret: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 50,
message: t('common.input_limit', [5, 50]),
trigger: 'blur',
},
],
callBack: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 10,
max: 100,
message: t('common.input_limit', [10, 100]),
trigger: 'blur',
},
{ required: true, validator: validateUrl, trigger: 'blur' },
],
enable: [
{
required: true,
message: t('common.require'),
trigger: 'change',
},
],
valid: [
{
required: true,
message: t('common.require'),
trigger: 'change',
},
],
})
const edit = (row: any) => {
state.form = {
appId: row.appId,
appSecret: row.appSecret,
callBack: row.callBack,
enable: row.enable,
valid: row.valid,
}
dialogVisible.value = true
}
const emits = defineEmits(['saved'])
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {
if (valid) {
const param = { ...state.form }
const method = request.post('/lark/create', param)
showLoading()
method
.then((res) => {
if (!res.msg) {
ElMessage.success(t('common.save_success'))
emits('saved')
reset()
}
closeLoading()
})
.catch(() => {
closeLoading()
})
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dialogVisible.value = false
}
const reset = () => {
resetForm(larkForm.value)
}
const showLoading = () => {
loadingInstance.value = ElLoading.service({
target: '.platform-info-drawer',
})
}
const closeLoading = () => {
loadingInstance.value?.close()
}
const validate = () => {
const url = '/lark/validate'
const data = state.form
showLoading()
request
.post(url, data)
.then(() => {
state.form.valid = 'true'
ElMessage.success(t('datasource.validate_success'))
})
.catch(() => {
state.form.enable = 'false'
state.form.valid = 'false'
})
.finally(() => {
closeLoading()
emits('saved')
})
}
defineExpose({
edit,
})
</script>
<template>
<el-drawer
v-model="dialogVisible"
:title="t('system.feishu_settings')"
modal-class="platform-info-drawer"
size="600px"
direction="rtl"
>
<el-form
ref="larkForm"
require-asterisk-position="right"
:model="state.form"
:rules="rule"
label-width="80px"
label-position="top"
>
<el-form-item label="APP Key" prop="appId">
<el-input v-model="state.form.appId" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="APP Secret" prop="appSecret">
<el-input
v-model="state.form.appSecret"
type="password"
show-password
:placeholder="t('common.please_input')"
/>
</el-form-item>
<el-form-item :label="t('system.callback_domain_name')" prop="callBack">
<el-input v-model="state.form.callBack" :placeholder="t('common.please_input')" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button secondary @click="resetForm(larkForm)">{{ t('common.cancel') }}</el-button>
<el-button
secondary
:disabled="!state.form.appId || !state.form.appSecret || !state.form.callBack"
@click="validate"
>
{{ t('commons.validate') }}
</el-button>
<el-button type="primary" @click="submitForm(larkForm)">
{{ t('commons.save') }}
</el-button>
</span>
</template>
</el-drawer>
</template>
<style lang="less">
.platform-info-drawer {
.ed-drawer__footer {
height: 64px !important;
padding: 16px 24px !important;
.dialog-footer {
height: 32px;
line-height: 32px;
}
}
.ed-form-item__label {
line-height: 22px !important;
height: 22px !important;
}
}
</style>
<style lang="less" scoped>
.platform-info-drawer {
.ed-form-item {
margin-bottom: 16px;
}
.is-error {
margin-bottom: 40px !important;
}
.input-with-select {
.ed-input-group__prepend {
width: 72px;
background-color: #fff;
padding: 0 20px;
color: #1f2329;
text-align: center;
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
}
}
</style>
@@ -0,0 +1,240 @@
<template>
<div class="platform-head-container" :class="{ 'just-head': !existInfo }">
<div class="platform-setting-head">
<div class="platform-setting-head-left">
<div class="lead-left-icon">
<el-icon>
<Icon name="logo_lark"><logo_lark class="svg-icon" /></Icon>
</el-icon>
<span>{{ t('user.feishu') }}</span>
</div>
<div v-if="existInfo" class="lead-left-status" :class="{ invalid: !info.valid }">
<span>{{
info.valid ? t('datasource.valid') : info.appId ? t('datasource.invalid') : ''
}}</span>
</div>
</div>
<div v-if="existInfo" class="platform-setting-head-right">
<span>{{ info.enable ? t('system.enabled') : t('system.close') }}</span>
<el-switch
v-if="info.valid"
v-model="info.enable"
class="status-switch"
@change="switchEnableApi"
/>
<el-tooltip
v-else
class="box-item"
effect="dark"
:content="t('platform.can_enable_it')"
placement="top"
>
<el-switch
v-model="info.enable"
disabled=""
class="status-switch"
@change="switchEnableApi"
/>
</el-tooltip>
</div>
<div v-else class="platform-setting-head-right-btn">
<el-button type="primary" @click="edit">{{ t('system.access') }}</el-button>
</div>
</div>
</div>
<InfoTemplate
v-if="existInfo"
class="platform-setting-main"
:copy-list="copyList"
setting-key="lark"
setting-title=""
:hide-head="true"
:setting-data="settingList"
@edit="edit"
/>
<div v-if="existInfo" class="platform-foot-container">
<el-button type="primary" @click="edit">
{{ t('commons.edit') }}
</el-button>
<el-button
secondary
:disabled="!info.appId || !info.appSecret || !info.callBack"
@click="validate"
>{{ t('commons.validate') }}</el-button
>
</div>
<lark-edit ref="editor" @saved="search" />
</template>
<script lang="ts" setup>
import logo_lark from '@/assets/svg/logo_lark.svg'
import { ref, reactive } from 'vue'
import InfoTemplate from '../common/InfoTemplate.vue'
import LarkEdit from './LarkEdit.vue'
import { request } from '@/utils/request'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus-secondary'
const existInfo = ref(false)
const { t } = useI18n()
const editor = ref()
const copyList = ['APP Key', 'APP Secret']
const settingList = reactive([
{
pkey: 'APP Key',
pval: 'smtp.exmail.qq.com',
type: 'text',
sort: 1,
},
{
pkey: 'APP Secret',
pval: 'smtp.exmail.qq.com',
type: 'pwd',
sort: 2,
},
{
pkey: t('system.callback_domain_name'),
pval: 'https://de.fit2cloud.com',
type: 'text',
sort: 3,
},
])
const info = ref({}) as any
const mappingArray = ['appId', 'appSecret', 'callBack', 'enable']
const search = () => {
const url = '/lark/info'
request.get(url).then((res) => {
info.value = res.data
existInfo.value = info.value['appId']
for (let index = 0; index < settingList.length; index++) {
const element = settingList[index]
const key = mappingArray[index]
if (index === 3) {
element['pval'] = res.data[key] ? t('chart.open') : t('chart.close')
} else {
element['pval'] = res.data[key] || '-'
}
}
})
}
const switchEnableApi = (enable: any) => {
const url = '/lark/switchEnable'
const data = { enable }
request.post(url, data).catch(() => {
info.value.enable = false
})
}
const edit = () => {
editor?.value.edit(info.value)
}
const validate = () => {
if (info.value?.appId && info.value?.appSecret && info.value?.callBack) {
validateHandler()
}
}
const validateHandler = () => {
const url = '/lark/validate'
const data = info.value
request
.post(url, data)
.then(() => {
info.value.valid = true
ElMessage.success(t('datasource.validate_success'))
})
.catch(() => {
info.value.enable = false
info.value.valid = false
})
}
search()
</script>
<style lang="less" scoped>
.platform-head-container {
height: 41px;
border-bottom: 1px solid #1f232926;
}
.just-head {
height: auto !important;
border: none !important;
}
.platform-setting-head {
height: 24px;
display: flex;
align-items: center;
justify-content: space-between;
.platform-setting-head-left {
display: flex;
.lead-left-icon {
display: flex;
line-height: 24px;
align-items: center;
i {
width: 24px;
height: 24px;
font-size: 20px;
}
span {
margin-left: 4px;
font-family: var(--de-custom_font, 'PingFang');
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 24px;
}
}
.lead-left-status {
margin-left: 4px;
height: 24px;
background: #34c72433;
padding: 0 6px;
font-size: 14px;
border-radius: 2px;
overflow: hidden;
span {
line-height: 24px;
color: #2ca91f;
}
}
.invalid {
background: #f54a4533 !important;
span {
color: #d03f3b !important;
}
}
}
.platform-setting-head-right-btn {
height: 32px;
line-height: 32px;
}
.platform-setting-head-right {
height: 22px;
line-height: 24px;
display: flex;
span {
margin-right: 8px;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.status-switch {
line-height: 22px !important;
height: 22px !important;
}
}
}
.platform-setting-main {
display: inline-block;
width: 100%;
padding: 16px 0 0 0 !important;
::v-deep(.info-template-content) {
display: contents !important;
}
}
.platform-foot-container {
height: 32px;
margin-top: -7px;
}
</style>
@@ -0,0 +1,262 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ElMessage, ElLoading } from 'element-plus-secondary'
import { request } from '@/utils/request'
import { useI18n } from 'vue-i18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
const { t } = useI18n()
const dialogVisible = ref(false)
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
const larksuiteForm = ref<FormInstance>()
interface LarksuiteForm {
appId?: string
appSecret?: string
callBack?: string
enable?: string
valid?: string
}
const state = reactive({
form: reactive<LarksuiteForm>({
appId: '',
appSecret: '',
callBack: '',
enable: 'false',
valid: 'false',
}),
})
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const validateUrl = (rule, value, callback) => {
const reg = new RegExp(/(http|https):\/\/([\w.]+\/?)\S*/)
if (!reg.test(value)) {
callback(new Error(t('system.incorrect_please_re_enter')))
} else {
callback()
}
}
const rule = reactive<FormRules>({
appId: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 20,
message: t('common.input_limit', [5, 20]),
trigger: 'blur',
},
],
appSecret: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 50,
message: t('common.input_limit', [5, 50]),
trigger: 'blur',
},
],
callBack: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 10,
max: 100,
message: t('common.input_limit', [10, 100]),
trigger: 'blur',
},
{ required: true, validator: validateUrl, trigger: 'blur' },
],
enable: [
{
required: true,
message: t('common.require'),
trigger: 'change',
},
],
valid: [
{
required: true,
message: t('common.require'),
trigger: 'change',
},
],
})
const edit = (row: any) => {
state.form = {
appId: row.appId,
appSecret: row.appSecret,
callBack: row.callBack,
enable: row.enable,
valid: row.valid,
}
dialogVisible.value = true
}
const emits = defineEmits(['saved'])
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {
if (valid) {
const param = { ...state.form }
const method = request.post('/larksuite/create', param)
showLoading()
method
.then((res) => {
if (!res.msg) {
ElMessage.success(t('common.save_success'))
emits('saved')
reset()
}
closeLoading()
})
.catch(() => {
closeLoading()
})
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dialogVisible.value = false
}
const reset = () => {
resetForm(larksuiteForm.value)
}
const showLoading = () => {
loadingInstance.value = ElLoading.service({
target: '.platform-info-drawer',
})
}
const closeLoading = () => {
loadingInstance.value?.close()
}
const validate = () => {
const url = '/larksuite/validate'
const data = state.form
showLoading()
request
.post(url, data)
.then(() => {
state.form.valid = 'true'
ElMessage.success(t('datasource.validate_success'))
})
.catch(() => {
state.form.enable = 'false'
state.form.valid = 'false'
})
.finally(() => {
closeLoading()
emits('saved')
})
}
defineExpose({
edit,
})
</script>
<template>
<el-drawer
v-model="dialogVisible"
:title="t('system.international_feishu_settings')"
modal-class="platform-info-drawer"
size="600px"
direction="rtl"
>
<el-form
ref="larksuiteForm"
require-asterisk-position="right"
:model="state.form"
:rules="rule"
label-width="80px"
label-position="top"
>
<el-form-item label="APP Key" prop="appId">
<el-input v-model="state.form.appId" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="APP Secret" prop="appSecret">
<el-input
v-model="state.form.appSecret"
type="password"
show-password
:placeholder="t('common.please_input')"
/>
</el-form-item>
<el-form-item :label="t('system.callback_domain_name')" prop="callBack">
<el-input v-model="state.form.callBack" :placeholder="t('common.please_input')" />
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button secondary @click="resetForm(larksuiteForm)">{{ t('common.cancel') }}</el-button>
<el-button
secondary
:disabled="!state.form.appId || !state.form.appSecret || !state.form.callBack"
@click="validate"
>
{{ t('commons.validate') }}
</el-button>
<el-button type="primary" @click="submitForm(larksuiteForm)">
{{ t('commons.save') }}
</el-button>
</span>
</template>
</el-drawer>
</template>
<style lang="less">
.platform-info-drawer {
.ed-drawer__footer {
height: 64px !important;
padding: 16px 24px !important;
.dialog-footer {
height: 32px;
line-height: 32px;
}
}
.ed-form-item__label {
line-height: 22px !important;
height: 22px !important;
}
}
</style>
<style lang="less" scoped>
.platform-info-drawer {
.ed-form-item {
margin-bottom: 16px;
}
.is-error {
margin-bottom: 40px !important;
}
.input-with-select {
.ed-input-group__prepend {
width: 72px;
background-color: #fff;
padding: 0 20px;
color: #1f2329;
text-align: center;
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
}
}
</style>
@@ -0,0 +1,241 @@
<template>
<div class="platform-head-container" :class="{ 'just-head': !existInfo }">
<div class="platform-setting-head">
<div class="platform-setting-head-left">
<div class="lead-left-icon">
<el-icon>
<Icon name="logo_lark"><logo_lark class="svg-icon" /></Icon>
</el-icon>
<span>{{ t('system.international_feishu') }}</span>
</div>
<div v-if="existInfo" class="lead-left-status" :class="{ invalid: !info.valid }">
<span>{{
info.valid ? t('datasource.valid') : info.appId ? t('datasource.invalid') : ''
}}</span>
</div>
</div>
<div v-if="existInfo" class="platform-setting-head-right">
<span>{{ info.enable ? t('system.enabled') : t('system.close') }}</span>
<el-switch
v-if="info.valid"
v-model="info.enable"
class="status-switch"
@change="switchEnableApi"
/>
<el-tooltip
v-else
class="box-item"
effect="dark"
:content="t('platform.can_enable_it')"
placement="top"
>
<el-switch
v-model="info.enable"
disabled=""
class="status-switch"
@change="switchEnableApi"
/>
</el-tooltip>
</div>
<div v-else class="platform-setting-head-right-btn">
<el-button type="primary" @click="edit">{{ t('system.access') }}</el-button>
</div>
</div>
</div>
<InfoTemplate
v-if="existInfo"
class="platform-setting-main"
:copy-list="copyList"
setting-key="larksuite"
setting-title=""
:hide-head="true"
:setting-data="settingList"
@edit="edit"
/>
<div v-if="existInfo" class="platform-foot-container">
<el-button type="primary" @click="edit">
{{ t('commons.edit') }}
</el-button>
<el-button
secondary
:disabled="!info.appId || !info.appSecret || !info.callBack"
@click="validate"
>{{ t('commons.validate') }}</el-button
>
</div>
<larksuite-edit ref="editor" @saved="search" />
</template>
<script lang="ts" setup>
import logo_lark from '@/assets/svg/logo_lark.svg'
import { ref, reactive } from 'vue'
import InfoTemplate from '../common/InfoTemplate.vue'
import LarksuiteEdit from './LarksuiteEdit.vue'
import { request } from '@/utils/request'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus-secondary'
const { t } = useI18n()
const existInfo = ref(false)
const editor = ref()
const copyList = ['APP Key', 'APP Secret']
const settingList = reactive([
{
pkey: 'APP Key',
pval: 'smtp.exmail.qq.com',
type: 'text',
sort: 1,
},
{
pkey: 'APP Secret',
pval: 'smtp.exmail.qq.com',
type: 'pwd',
sort: 2,
},
{
pkey: t('system.callback_domain_name'),
pval: 'https://de.fit2cloud.com',
type: 'text',
sort: 3,
},
])
const info = ref({}) as any
const mappingArray = ['appId', 'appSecret', 'callBack', 'enable']
const search = () => {
const url = '/larksuite/info'
request.get(url).then((res) => {
info.value = res.data
existInfo.value = info.value['appId']
for (let index = 0; index < settingList.length; index++) {
const element = settingList[index]
const key = mappingArray[index]
if (index === 3) {
element['pval'] = res.data[key] ? t('chart.open') : t('chart.close')
} else {
element['pval'] = res.data[key] || '-'
}
}
})
}
const switchEnableApi = (enable: any) => {
const url = '/larksuite/switchEnable'
const data = { enable }
request.post(url, data).catch(() => {
info.value.enable = false
})
}
const edit = () => {
editor?.value.edit(info.value)
}
const validate = () => {
if (info.value?.appId && info.value?.appSecret && info.value?.callBack) {
validateHandler()
}
}
const validateHandler = () => {
const url = '/larksuite/validate'
const data = info.value
request
.post(url, data)
.then(() => {
info.value.valid = true
ElMessage.success(t('datasource.validate_success'))
})
.catch(() => {
info.value.enable = false
info.value.valid = false
})
}
search()
</script>
<style lang="less" scoped>
.platform-head-container {
height: 41px;
border-bottom: 1px solid #1f232926;
}
.just-head {
height: auto !important;
border: none !important;
}
.platform-setting-head {
height: 24px;
display: flex;
align-items: center;
justify-content: space-between;
.platform-setting-head-left {
display: flex;
.lead-left-icon {
display: flex;
line-height: 24px;
align-items: center;
i {
width: 24px;
height: 24px;
font-size: 20px;
}
span {
margin-left: 4px;
font-family: var(--de-custom_font, 'PingFang');
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 24px;
}
}
.lead-left-status {
margin-left: 4px;
height: 24px;
background: #34c72433;
padding: 0 6px;
font-size: 14px;
border-radius: 2px;
overflow: hidden;
span {
line-height: 24px;
color: #2ca91f;
}
}
.invalid {
background: #f54a4533 !important;
span {
color: #d03f3b !important;
}
}
}
.platform-setting-head-right-btn {
height: 32px;
line-height: 32px;
}
.platform-setting-head-right {
height: 22px;
line-height: 24px;
display: flex;
span {
margin-right: 8px;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.status-switch {
line-height: 22px !important;
height: 22px !important;
}
}
}
.platform-setting-main {
display: inline-block;
width: 100%;
padding: 16px 0 0 0 !important;
::v-deep(.info-template-content) {
display: contents !important;
}
}
.platform-foot-container {
height: 32px;
margin-top: -7px;
}
</style>
@@ -0,0 +1,258 @@
<script lang="ts" setup>
import { ref, reactive } from 'vue'
import { ElMessage, ElLoading } from 'element-plus-secondary'
import { request } from '@/utils/request'
import { useI18n } from 'vue-i18n'
import type { FormInstance, FormRules } from 'element-plus-secondary'
const { t } = useI18n()
const dialogVisible = ref(false)
const loadingInstance = ref<ReturnType<typeof ElLoading.service> | null>(null)
const wecomForm = ref<FormInstance>()
interface WecomkForm {
corpid?: string
agent_id?: string
corpsecret?: string
}
const state = reactive({
form: reactive<WecomkForm>({
agent_id: '',
corpid: '',
corpsecret: '',
}),
})
const origin = ref(6)
const id = ref()
const rule = reactive<FormRules>({
agent_id: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 20,
message: t('common.input_limit', [5, 20]),
trigger: 'blur',
},
],
corpid: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 20,
message: t('common.input_limit', [5, 20]),
trigger: 'blur',
},
],
corpsecret: [
{
required: true,
message: t('common.require'),
trigger: 'blur',
},
{
min: 5,
max: 100,
message: t('common.input_limit', [5, 100]),
trigger: 'blur',
},
],
})
const edit = (row: any) => {
state.form = {
agent_id: row.agent_id,
corpid: row.corpid,
corpsecret: row.corpsecret,
}
if (row?.id) {
id.value = row.id
}
dialogVisible.value = true
}
const emits = defineEmits(['saved'])
const submitForm = async (formEl: FormInstance | undefined) => {
if (!formEl) return
await formEl.validate((valid) => {
if (valid) {
const param = { ...state.form }
const data = {
id: origin.value,
type: origin.value,
name: 'wecom',
config: JSON.stringify(param),
}
const method = id.value
? request.put('/system/authentication', data, { requestOptions: { silent: true } })
: request.post('/system/authentication', data, { requestOptions: { silent: true } })
showLoading()
method
.then((res) => {
if (!res.msg) {
ElMessage.success(t('common.save_success'))
emits('saved')
reset()
}
})
.catch((e: any) => {
if (
e.message?.startsWith('sqlbot_authentication_connect_error') ||
e.response?.data?.startsWith('sqlbot_authentication_connect_error')
) {
emits('saved')
ElMessage.error(t('ds.connection_failed'))
}
})
.finally(() => {
closeLoading()
})
}
})
}
const resetForm = (formEl: FormInstance | undefined) => {
if (!formEl) return
formEl.resetFields()
dialogVisible.value = false
id.value = null
}
const reset = () => {
resetForm(wecomForm.value)
}
const showLoading = () => {
loadingInstance.value = ElLoading.service({
target: '.platform-info-drawer',
})
}
const closeLoading = () => {
loadingInstance.value?.close()
}
const validate = () => {
const url = '/system/authentication/status'
const config_data = state.form
const data = {
type: origin.value,
name: 'wecom',
config: JSON.stringify(config_data),
}
showLoading()
request
.patch(url, data)
.then((res) => {
if (res) {
ElMessage.success(t('ds.connection_success'))
} else {
ElMessage.error(t('ds.connection_failed'))
}
})
.finally(() => {
closeLoading()
emits('saved')
})
}
defineExpose({
edit,
})
</script>
<template>
<el-drawer
v-model="dialogVisible"
:title="t('user.wechat_for_business')"
modal-class="platform-info-drawer"
size="600px"
direction="rtl"
>
<el-form
ref="wecomForm"
require-asterisk-position="right"
:model="state.form"
:rules="rule"
label-width="80px"
label-position="top"
>
<el-form-item label="Corpid" prop="corpid">
<el-input v-model="state.form.corpid" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="AgentId" prop="agent_id">
<el-input v-model="state.form.agent_id" :placeholder="t('common.please_input')" />
</el-form-item>
<el-form-item label="APP Secret" prop="corpsecret">
<el-input
v-model="state.form.corpsecret"
type="password"
show-password
:placeholder="t('common.please_input')"
/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="resetForm(wecomForm)">{{ t('common.cancel') }}</el-button>
<el-button
:disabled="!state.form.corpid || !state.form.corpsecret || !state.form.agent_id"
@click="validate"
>
{{ t('ds.check') }}
</el-button>
<el-button type="primary" @click="submitForm(wecomForm)">
{{ t('common.save') }}
</el-button>
</span>
</template>
</el-drawer>
</template>
<style lang="less">
.platform-info-drawer {
.ed-drawer__footer {
height: 64px !important;
padding: 16px 24px !important;
.dialog-footer {
height: 32px;
line-height: 32px;
}
}
.ed-form-item__label {
line-height: 22px !important;
height: 22px !important;
}
}
</style>
<style lang="less" scoped>
.platform-info-drawer {
.ed-form-item {
margin-bottom: 16px;
}
.is-error {
margin-bottom: 40px !important;
}
.input-with-select {
.ed-input-group__prepend {
width: 72px;
background-color: #fff;
padding: 0 20px;
color: #1f2329;
text-align: center;
font-family: var(--de-custom_font, 'PingFang');
font-size: 14px;
font-style: normal;
font-weight: 400;
line-height: 22px;
}
}
}
</style>
@@ -0,0 +1,247 @@
<template>
<div class="platform-head-container" :class="{ 'just-head': !existInfo }">
<div class="platform-setting-head">
<div class="platform-setting-head-left">
<div class="lead-left-icon">
<el-icon>
<Icon name="logo_wechat-work"><logo_wechatWork class="svg-icon" /></Icon>
</el-icon>
<span>{{ t('user.wechat_for_business') }}</span>
</div>
<div v-if="existInfo" class="lead-left-status" :class="{ invalid: !info.valid }">
<span>{{
info.valid ? t('authentication.valid') : info.corpid ? t('authentication.invalid') : ''
}}</span>
</div>
</div>
<div v-if="existInfo" class="platform-setting-head-right">
<span>{{ info.enable ? t('platform.status_open') : t('platform.status_close') }}</span>
<el-switch
v-if="info.valid"
v-model="info.enable"
class="status-switch"
@change="switchEnableApi"
/>
<el-tooltip
v-else
class="box-item"
effect="dark"
:content="t('platform.can_enable_it')"
placement="top"
>
<el-switch
v-model="info.enable"
disabled=""
class="status-switch"
@change="switchEnableApi"
/>
</el-tooltip>
</div>
<div v-else class="platform-setting-head-right-btn">
<el-button type="primary" @click="edit">{{ t('platform.access_in') }}</el-button>
</div>
</div>
</div>
<InfoTemplate
v-if="existInfo"
class="platform-setting-main"
:copy-list="copyList"
setting-key="wecom"
setting-title=""
:hide-head="true"
:setting-data="settingList"
@edit="edit"
/>
<div v-if="existInfo" class="platform-foot-container">
<el-button type="primary" @click="edit">
{{ t('datasource.edit') }}
</el-button>
<el-button secondary :disabled="!info.corpid || !info.corpsecret" @click="validate">{{
t('ds.check')
}}</el-button>
</div>
<wecom-edit ref="editor" @saved="search" />
</template>
<script lang="ts" setup>
import logo_wechatWork from '@/assets/svg/logo_wechat-work.svg'
import { ref, reactive } from 'vue'
import InfoTemplate from '../common/InfoTemplate.vue'
import WecomEdit from './WecomEdit.vue'
import { request } from '@/utils/request'
import { useI18n } from 'vue-i18n'
import { ElMessage } from 'element-plus-secondary'
const { t } = useI18n()
const existInfo = ref(false)
const editor = ref()
const origin = ref(6)
const copyList = ['CorpId', 'APP Secret']
const settingList = reactive([
{
pkey: 'CorpId',
pval: '',
type: 'text',
sort: 1,
},
{
pkey: 'AgentId',
pval: '',
type: 'text',
sort: 2,
},
{
pkey: 'APP Secret',
pval: '',
type: 'pwd',
sort: 3,
},
])
const info = ref({}) as any
const mappingArray = ['corpid', 'agent_id', 'corpsecret', 'enable']
const search = () => {
const url = `system/authentication/${origin.value}`
request.get(url).then((res) => {
const config = res?.config
let configData = { enable: res?.enable, valid: res?.valid } as any
if (config) {
const configObj = JSON.parse(config)
Object.assign(configData, configObj)
}
info.value = configData
if (res?.id) {
info.value['id'] = res.id
}
existInfo.value = info.value['agent_id']
for (let index = 0; index < settingList.length; index++) {
const element = settingList[index]
const key = mappingArray[index]
element['pval'] = configData[key] || '-'
}
})
}
const switchEnableApi = (enable: any) => {
const url = '/system/authentication/enable'
const data = { id: origin.value, enable }
request.patch(url, data).catch(() => {
info.value.enable = false
})
}
const edit = () => {
editor?.value.edit(info.value)
}
const validate = () => {
if (info.value?.agent_id && info.value?.corpsecret) {
validateHandler()
}
}
const validateHandler = () => {
request
.patch('/system/authentication/status', { type: origin.value, name: '', config: '' })
.then((res) => {
if (res) {
info.value.valid = true
ElMessage.success(t('ds.connection_success'))
} else {
ElMessage.error(t('ds.connection_failed'))
info.value.enable = false
info.value.valid = false
}
})
.catch(() => {
info.value.enable = false
info.value.valid = false
})
}
search()
</script>
<style lang="less" scoped>
.platform-head-container {
height: 41px;
border-bottom: 1px solid #1f232926;
}
.just-head {
height: auto !important;
border: none !important;
}
.platform-setting-head {
height: 24px;
display: flex;
align-items: center;
justify-content: space-between;
.platform-setting-head-left {
display: flex;
.lead-left-icon {
display: flex;
line-height: 24px;
align-items: center;
i {
width: 24px;
height: 24px;
font-size: 20px;
}
span {
margin-left: 4px;
font-family: var(--de-custom_font, 'PingFang');
font-size: 16px;
font-style: normal;
font-weight: 500;
line-height: 24px;
}
}
.lead-left-status {
margin-left: 4px;
height: 24px;
background: #34c72433;
padding: 0 6px;
font-size: 14px;
border-radius: 2px;
overflow: hidden;
span {
line-height: 24px;
color: #2ca91f;
}
}
.invalid {
background: #f54a4533 !important;
span {
color: #d03f3b !important;
}
}
}
.platform-setting-head-right-btn {
height: 32px;
line-height: 32px;
}
.platform-setting-head-right {
height: 22px;
line-height: 24px;
display: flex;
span {
margin-right: 8px;
font-size: 14px;
height: 22px;
line-height: 22px;
}
.status-switch {
line-height: 22px !important;
height: 22px !important;
}
}
}
.platform-setting-main {
display: inline-block;
width: 100%;
padding: 16px 0 0 0 !important;
::v-deep(.info-template-content) {
display: contents !important;
}
}
.platform-foot-container {
height: 32px;
margin-top: -7px;
}
</style>