mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2025-07-19 12:03:37 +00:00
655 lines
24 KiB
Vue
655 lines
24 KiB
Vue
<template>
|
|
<div ref="headerBox" class="title">
|
|
<t-divider content="网络配置" align="left">
|
|
<template #content>
|
|
<div style="display: flex; justify-content: center; align-items: center">
|
|
<wifi1-icon />
|
|
<div style="margin-left: 5px">网络配置</div>
|
|
</div>
|
|
</template>
|
|
</t-divider>
|
|
<t-divider align="right">
|
|
<t-button @click="addConfig()">
|
|
<template #icon><add-icon /></template>
|
|
添加配置</t-button
|
|
>
|
|
</t-divider>
|
|
</div>
|
|
<div v-if="loadPage" ref="setting" class="setting">
|
|
<t-tabs ref="tabsRef" :style="{ width: tabsWidth + 'px' }" default-value="all" @change="selectType">
|
|
<t-tab-panel value="all" label="全部"></t-tab-panel>
|
|
<t-tab-panel value="httpServers" label="HTTP 服务器"></t-tab-panel>
|
|
<t-tab-panel value="httpClients" label="HTTP 客户端"></t-tab-panel>
|
|
<t-tab-panel value="websocketServers" label="WebSocket 服务器"></t-tab-panel>
|
|
<t-tab-panel value="websocketClients" label="WebSocket 客户端"></t-tab-panel>
|
|
</t-tabs>
|
|
</div>
|
|
<t-loading attach="#alice" :loading="!loadPage" :showOverlay="false">
|
|
<div id="alice" v-if="!loadPage" style="height: 80vh;position: relative" ></div>
|
|
</t-loading>
|
|
<div v-if="loadPage" class="card-box" :style="{ width: tabsWidth + 'px' }">
|
|
<div class="setting-box" :style="{ maxHeight: cardHeight + 'px' }" v-if="cardConfig.length > 0">
|
|
<div v-for="(item, index) in cardConfig" :key="index">
|
|
<t-card
|
|
:title="item.name"
|
|
:description="item.type"
|
|
:style="{ width: cardWidth + 'px' }"
|
|
:header-bordered="true"
|
|
class="setting-card"
|
|
>
|
|
<template #actions>
|
|
<t-space>
|
|
<edit2-icon size="20px" @click="editConfig(item)"></edit2-icon>
|
|
<t-popconfirm content="确认删除" @confirm="delConfig(item)">
|
|
<delete-icon size="20px"></delete-icon>
|
|
</t-popconfirm>
|
|
</t-space>
|
|
</template>
|
|
<div class="setting-content">
|
|
<t-card
|
|
class="card-address"
|
|
:style="{
|
|
borderLeft:
|
|
'7px solid ' + (item.enable ? 'var(--td-success-color)' : 'var(--td-error-color)'),
|
|
}"
|
|
>
|
|
<div class="local-box" v-if="item.host && item.port">
|
|
<server-filled-icon class="local-icon" size="20px" @click="toggleProperty(item, 'enable')"></server-filled-icon>
|
|
<strong class="local">{{ item.host }}:{{ item.port }}</strong>
|
|
<copy-icon
|
|
class="copy-icon"
|
|
size="20px"
|
|
@click="copyText(item.host + ':' + item.port)"
|
|
></copy-icon>
|
|
</div>
|
|
<div class="local-box" v-if="item.url">
|
|
<server-filled-icon class="local-icon" size="20px" @click="toggleProperty(item, 'enable')"></server-filled-icon>
|
|
<strong class="local">{{ item.url }}</strong>
|
|
<copy-icon class="copy-icon" size="20px" @click="copyText(item.url)"></copy-icon>
|
|
</div>
|
|
</t-card>
|
|
<t-collapse :default-value="[0]" expand-mutex style="margin-top: 10px" class="info-coll">
|
|
<t-collapse-panel header="基础信息">
|
|
<t-descriptions
|
|
size="small"
|
|
:layout="infoOneCol ? 'vertical' : 'horizontal'"
|
|
class="setting-base-info"
|
|
>
|
|
<t-descriptions-item v-if="item.token" label="连接密钥">
|
|
<div v-if="mediumScreen.matches || largeScreen.matches" class="token-view">
|
|
<span>{{ showToken ? item.token : '******' }}</span>
|
|
<browse-icon
|
|
class="browse-icon"
|
|
v-if="showToken"
|
|
size="18px"
|
|
@click="showToken = false"
|
|
></browse-icon>
|
|
<browse-off-icon
|
|
class="browse-icon"
|
|
v-else
|
|
size="18px"
|
|
@click="showToken = true"
|
|
></browse-off-icon>
|
|
</div>
|
|
<div v-else>
|
|
<t-popup :showArrow="true" trigger="click">
|
|
<t-tag theme="primary">点击查看</t-tag>
|
|
<template #content>
|
|
<div @click="copyText(item.token)">{{ item.token }}</div>
|
|
</template>
|
|
</t-popup>
|
|
</div>
|
|
</t-descriptions-item>
|
|
<t-descriptions-item label="消息格式">{{
|
|
item.messagePostFormat
|
|
}}</t-descriptions-item>
|
|
</t-descriptions>
|
|
</t-collapse-panel>
|
|
<t-collapse-panel header="状态信息">
|
|
<t-descriptions
|
|
size="small"
|
|
:layout="infoOneCol ? 'vertical' : 'horizontal'"
|
|
class="setting-base-info"
|
|
>
|
|
<t-descriptions-item v-if="item.hasOwnProperty('debug')" label="调试日志">
|
|
<t-tag
|
|
:class="item.debug ? 'tag-item-on' : 'tag-item-off'"
|
|
@click="toggleProperty(item, 'debug')"
|
|
>
|
|
{{ item.debug ? '开启' : '关闭' }}</t-tag
|
|
>
|
|
</t-descriptions-item>
|
|
<t-descriptions-item
|
|
v-if="item.hasOwnProperty('enableWebsocket')"
|
|
label="Websocket 功能"
|
|
>
|
|
<t-tag
|
|
:class="item.enableWebsocket ? 'tag-item-on' : 'tag-item-off'"
|
|
@click="toggleProperty(item, 'enableWebsocket')"
|
|
>
|
|
{{ item.enableWebsocket ? '启用' : '禁用' }}</t-tag
|
|
>
|
|
</t-descriptions-item>
|
|
<t-descriptions-item
|
|
v-if="item.hasOwnProperty('enableCors')"
|
|
label="跨域放行"
|
|
>
|
|
<t-tag :class="item.enableCors ? 'tag-item-on' : 'tag-item-off'" @click="toggleProperty(item, 'enableCors')">
|
|
{{ item.enableCors ? '开启' : '关闭' }}</t-tag
|
|
>
|
|
</t-descriptions-item>
|
|
<t-descriptions-item
|
|
v-if="item.hasOwnProperty('enableForcePushEvent')"
|
|
label="上报自身消息"
|
|
>
|
|
<t-tag
|
|
:class="item.reportSelfMessage ? 'tag-item-on' : 'tag-item-off'"
|
|
@click="toggleProperty(item, 'reportSelfMessage')"
|
|
>
|
|
{{ item.reportSelfMessage ? '开启' : '关闭' }}</t-tag
|
|
>
|
|
</t-descriptions-item>
|
|
<t-descriptions-item
|
|
v-if="item.hasOwnProperty('enableForcePushEvent')"
|
|
label="强制推送事件"
|
|
>
|
|
<t-tag
|
|
class="tag-item"
|
|
:class="item.enableForcePushEvent ? 'tag-item-on' : 'tag-item-off'"
|
|
@click="toggleProperty(item, 'enableForcePushEvent')"
|
|
>
|
|
{{ item.enableForcePushEvent ? '开启' : '关闭' }}</t-tag
|
|
>
|
|
</t-descriptions-item>
|
|
</t-descriptions>
|
|
</t-collapse-panel>
|
|
</t-collapse>
|
|
</div>
|
|
</t-card>
|
|
</div>
|
|
<div style="height: 20vh"></div>
|
|
</div>
|
|
<t-card v-else>
|
|
<t-empty class="card-none" title="暂无网络配置"> </t-empty>
|
|
</t-card>
|
|
</div>
|
|
<t-dialog
|
|
v-model:visible="visibleBody"
|
|
:header="dialogTitle"
|
|
:destroy-on-close="true"
|
|
:show-in-attached-element="true"
|
|
:on-confirm="saveConfig"
|
|
class=".t-dialog__ctx .t-dialog__position"
|
|
>
|
|
<div slot="body" class="dialog-body">
|
|
<t-form ref="form" :data="newTab" labelAlign="left" :model="newTab">
|
|
<t-form-item
|
|
style="text-align: left"
|
|
:rules="[{ required: true, message: '请输入名称', trigger: 'blur' }]"
|
|
label="名称"
|
|
name="name"
|
|
>
|
|
<t-input v-model="newTab.name" />
|
|
</t-form-item>
|
|
<t-form-item
|
|
style="text-align: left"
|
|
:rules="[{ required: true, message: '请选择类型', trigger: 'change' }]"
|
|
label="类型"
|
|
name="type"
|
|
>
|
|
<t-select v-model="newTab.type" @change="onloadDefault">
|
|
<t-option value="httpServers">HTTP 服务器</t-option>
|
|
<t-option value="httpClients">HTTP 客户端</t-option>
|
|
<t-option value="websocketServers">WebSocket 服务器</t-option>
|
|
<t-option value="websocketClients">WebSocket 客户端</t-option>
|
|
</t-select>
|
|
</t-form-item>
|
|
<div>
|
|
<component
|
|
:is="resolveDynamicComponent(getComponent(newTab.type as ComponentKey))"
|
|
:config="newTab.data"
|
|
/>
|
|
</div>
|
|
</t-form>
|
|
</div>
|
|
</t-dialog>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import {
|
|
AddIcon,
|
|
DeleteIcon,
|
|
Edit2Icon,
|
|
ServerFilledIcon,
|
|
CopyIcon,
|
|
BrowseOffIcon,
|
|
BrowseIcon,
|
|
Wifi1Icon,
|
|
} from 'tdesign-icons-vue-next';
|
|
import { onMounted, onUnmounted, ref, resolveDynamicComponent, watch } from 'vue';
|
|
import emitter from '@/ts/event-bus';
|
|
import {
|
|
mergeNetworkDefaultConfig,
|
|
mergeOneBotConfigs,
|
|
NetworkConfig,
|
|
OneBotConfig,
|
|
} from '../../../src/onebot/config/config';
|
|
import HttpServerComponent from '@/pages/network/HttpServerComponent.vue';
|
|
import HttpClientComponent from '@/pages/network/HttpClientComponent.vue';
|
|
import WebsocketServerComponent from '@/pages/network/WebsocketServerComponent.vue';
|
|
import WebsocketClientComponent from '@/pages/network/WebsocketClientComponent.vue';
|
|
import { MessagePlugin } from 'tdesign-vue-next';
|
|
import { QQLoginManager } from '@/backend/shell';
|
|
|
|
const showToken = ref<boolean>(false);
|
|
const infoOneCol = ref<boolean>(true);
|
|
const tabsWidth = ref<number>(0);
|
|
const menuWidth = ref<number>(0);
|
|
const cardWidth = ref<number>(0);
|
|
const cardHeight = ref<number>(0);
|
|
const mediumScreen = window.matchMedia('(min-width: 768px) and (max-width: 1024px)');
|
|
const largeScreen = window.matchMedia('(min-width: 1025px)');
|
|
const headerBox = ref<HTMLDivElement | null>(null);
|
|
const setting = ref<HTMLDivElement | null>(null);
|
|
const loadPage = ref<boolean>(false);
|
|
const visibleBody = ref<boolean>(false);
|
|
const newTab = ref<{ name: string; data: any; type: string }>({ name: '', data: {}, type: '' });
|
|
const dialogTitle = ref<string>('');
|
|
|
|
type ComponentKey = keyof typeof mergeNetworkDefaultConfig;
|
|
|
|
const componentMap: Record<
|
|
ComponentKey,
|
|
| typeof HttpServerComponent
|
|
| typeof HttpClientComponent
|
|
| typeof WebsocketServerComponent
|
|
| typeof WebsocketClientComponent
|
|
> = {
|
|
httpServers: HttpServerComponent,
|
|
httpClients: HttpClientComponent,
|
|
websocketServers: WebsocketServerComponent,
|
|
websocketClients: WebsocketClientComponent,
|
|
};
|
|
|
|
//操作类型
|
|
const operateType = ref<string>('');
|
|
//配置项索引
|
|
const configIndex = ref<number>(0);
|
|
//保存时所用数据
|
|
const networkConfig: NetworkConfig & { [key: string]: any } = {
|
|
websocketClients: [],
|
|
websocketServers: [],
|
|
httpClients: [],
|
|
httpServers: [],
|
|
};
|
|
|
|
//挂载的数据
|
|
const WebConfg = ref(
|
|
new Map<string, Array<null>>([
|
|
['all', []],
|
|
['httpServers', []],
|
|
['httpClients', []],
|
|
['websocketServers', []],
|
|
['websocketClients', []],
|
|
])
|
|
);
|
|
const typeCh: Record<ComponentKey, string> = {
|
|
httpServers: 'HTTP 服务器',
|
|
httpClients: 'HTTP 客户端',
|
|
websocketServers: 'WebSocket 服务器',
|
|
websocketClients: 'WebSocket 客户端',
|
|
};
|
|
const cardConfig = ref<any>([]);
|
|
const getComponent = (type: ComponentKey) => {
|
|
return componentMap[type];
|
|
};
|
|
const getKeyByValue = (obj: typeof typeCh, value: string): string | undefined => {
|
|
return Object.entries(obj).find(([_, v]) => v === value)?.[0];
|
|
};
|
|
|
|
const addConfig = () => {
|
|
dialogTitle.value = '添加配置';
|
|
newTab.value = { name: '', data: {}, type: '' };
|
|
operateType.value = 'add';
|
|
visibleBody.value = true;
|
|
};
|
|
|
|
const editConfig = (item: any) => {
|
|
dialogTitle.value = '修改配置';
|
|
const type = getKeyByValue(typeCh, item.type);
|
|
if (type) {
|
|
newTab.value = { name: item.name, data: item, type: type };
|
|
}
|
|
operateType.value = 'edit';
|
|
configIndex.value = networkConfig[newTab.value.type].findIndex((obj: any) => obj.name === item.name);
|
|
visibleBody.value = true;
|
|
};
|
|
const toggleProperty = async (item: any, tagData: string) => {
|
|
const type = getKeyByValue(typeCh, item.type);
|
|
const newData = { ...item };
|
|
newData[tagData] = !item[tagData];
|
|
if (type) {
|
|
newTab.value = { name: item.name, data: newData, type: type };
|
|
}
|
|
operateType.value = 'edit';
|
|
configIndex.value = networkConfig[newTab.value.type].findIndex((obj: any) => obj.name === item.name);
|
|
await saveConfig();
|
|
};
|
|
|
|
const delConfig = (item: any) => {
|
|
const type = getKeyByValue(typeCh, item.type);
|
|
if (type) {
|
|
newTab.value = { name: item.name, data: item, type: type };
|
|
}
|
|
configIndex.value = configIndex.value = networkConfig[newTab.value.type].findIndex(
|
|
(obj: any) => obj.name === item.name
|
|
);
|
|
operateType.value = 'delete';
|
|
saveConfig();
|
|
};
|
|
|
|
const selectType = (key: ComponentKey) => {
|
|
cardConfig.value = WebConfg.value.get(key);
|
|
};
|
|
|
|
const onloadDefault = (key: ComponentKey) => {
|
|
newTab.value.data = structuredClone(mergeNetworkDefaultConfig[key]);
|
|
};
|
|
//检测重名
|
|
const checkName = (name: string) => {
|
|
const allConfigs = WebConfg.value.get('all')?.findIndex((obj: any) => obj.name === name);
|
|
if (newTab.value.name === '' || newTab.value.type === '') {
|
|
MessagePlugin.error('请填写完整信息');
|
|
return false;
|
|
} else if (allConfigs === -1 || newTab.value.data.name === name) {
|
|
return true;
|
|
} else {
|
|
MessagePlugin.error('名称已存在');
|
|
return false;
|
|
}
|
|
};
|
|
//保存
|
|
const saveConfig = async () => {
|
|
if (operateType.value == 'add') {
|
|
if (!checkName(newTab.value.name)) return;
|
|
newTab.value.data.name = newTab.value.name;
|
|
networkConfig[newTab.value.type].push(newTab.value.data);
|
|
} else if (operateType.value == 'edit') {
|
|
if (!checkName(newTab.value.name)) return;
|
|
newTab.value.data.name = newTab.value.name;
|
|
networkConfig[newTab.value.type][configIndex.value] = newTab.value.data;
|
|
} else if (operateType.value == 'delete') {
|
|
networkConfig[newTab.value.type].splice(configIndex.value, 1);
|
|
}
|
|
const userConfig = await getOB11Config();
|
|
if (!userConfig) return;
|
|
userConfig.network = networkConfig;
|
|
const success = await setOB11Config(userConfig);
|
|
if (success) {
|
|
operateType.value = '';
|
|
configIndex.value = 0;
|
|
MessagePlugin.success('配置保存成功');
|
|
await loadConfig();
|
|
visibleBody.value = false;
|
|
} else {
|
|
MessagePlugin.error('配置保存失败');
|
|
}
|
|
};
|
|
const getOB11Config = async (): Promise<OneBotConfig | undefined> => {
|
|
const storedCredential = localStorage.getItem('auth');
|
|
if (!storedCredential) {
|
|
console.error('No stored credential found');
|
|
return;
|
|
}
|
|
const loginManager = new QQLoginManager(storedCredential);
|
|
return await loginManager.GetOB11Config();
|
|
};
|
|
|
|
const setOB11Config = async (config: OneBotConfig): Promise<boolean> => {
|
|
const storedCredential = localStorage.getItem('auth');
|
|
if (!storedCredential) {
|
|
console.error('No stored credential found');
|
|
return false;
|
|
}
|
|
const loginManager = new QQLoginManager(storedCredential);
|
|
return await loginManager.SetOB11Config(config);
|
|
};
|
|
|
|
//获取卡片数据
|
|
const getAllData = (data: NetworkConfig) => {
|
|
cardConfig.value = [];
|
|
WebConfg.value.set('all', []);
|
|
for (const key in data) {
|
|
const configs = data[key as keyof NetworkConfig];
|
|
if (key in mergeNetworkDefaultConfig) {
|
|
networkConfig[key] = [...configs];
|
|
const newConfigsArray = configs.map((config: any) => ({
|
|
...config,
|
|
type: typeCh[key as ComponentKey],
|
|
}));
|
|
WebConfg.value.set(key, newConfigsArray);
|
|
const allConfigs = WebConfg.value.get('all');
|
|
if (allConfigs) {
|
|
const newAllConfigs = [...allConfigs, ...newConfigsArray];
|
|
WebConfg.value.set('all', newAllConfigs);
|
|
}
|
|
cardConfig.value = WebConfg.value.get('all');
|
|
}
|
|
}
|
|
};
|
|
|
|
const loadConfig = async () => {
|
|
try {
|
|
const userConfig = await getOB11Config();
|
|
if (!userConfig) return;
|
|
const mergedConfig = mergeOneBotConfigs(userConfig);
|
|
getAllData(mergedConfig.network);
|
|
} catch (error) {
|
|
console.error('Error loading config:', error);
|
|
}
|
|
};
|
|
|
|
const copyText = async (text: string) => {
|
|
const textarea = document.createElement('textarea');
|
|
textarea.value = text;
|
|
document.body.appendChild(textarea);
|
|
textarea.select();
|
|
try {
|
|
document.execCommand('copy');
|
|
MessagePlugin.success('复制成功');
|
|
} catch (err) {
|
|
console.error('复制失败', err);
|
|
} finally {
|
|
document.body.removeChild(textarea);
|
|
}
|
|
};
|
|
|
|
const handleResize = () => {
|
|
tabsWidth.value = window.innerWidth - 41 - menuWidth.value;
|
|
if (mediumScreen.matches) {
|
|
cardWidth.value = (tabsWidth.value - 20) / 2;
|
|
} else if (largeScreen.matches) {
|
|
cardWidth.value = (tabsWidth.value - 40) / 3;
|
|
} else {
|
|
cardWidth.value = tabsWidth.value;
|
|
}
|
|
loadPage.value = true;
|
|
setTimeout(()=>{
|
|
cardHeight.value = window.innerHeight - (headerBox.value?.offsetHeight ?? 0) - (setting.value?.offsetHeight ?? 0) - 21;
|
|
},300)
|
|
};
|
|
emitter.on('sendWidth', (width) => {
|
|
if (typeof width === 'string') {
|
|
const strWidth = width as string;
|
|
menuWidth.value = parseInt(strWidth);
|
|
}
|
|
});
|
|
watch(menuWidth, (newValue, oldValue) => {
|
|
loadPage.value = false;
|
|
setTimeout(()=>{
|
|
handleResize();
|
|
},300)
|
|
});
|
|
onMounted(() => {
|
|
loadConfig();
|
|
const cachedWidth = localStorage.getItem('menuWidth');
|
|
if (cachedWidth) {
|
|
menuWidth.value = parseInt(cachedWidth);
|
|
setTimeout(()=>{
|
|
handleResize();
|
|
},300)
|
|
}
|
|
window.addEventListener('resize', ()=>{
|
|
setTimeout(()=>{
|
|
handleResize();
|
|
},300)
|
|
});
|
|
});
|
|
onUnmounted(() => {
|
|
window.removeEventListener('resize', ()=>{
|
|
setTimeout(()=>{
|
|
handleResize();
|
|
},300)
|
|
});
|
|
});
|
|
</script>
|
|
|
|
<style scoped>
|
|
.title {
|
|
padding: 20px 20px 0 20px;
|
|
display: flex;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.setting {
|
|
margin: 0 20px;
|
|
}
|
|
|
|
.setting-box {
|
|
display: grid;
|
|
grid-template-columns: 1fr 1fr 1fr;
|
|
gap: 20px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.setting-card {
|
|
width: 100%;
|
|
text-align: left;
|
|
}
|
|
|
|
.setting-content {
|
|
width: 100%;
|
|
}
|
|
|
|
.card-address svg {
|
|
fill: var(--td-brand-color);
|
|
cursor: pointer;
|
|
}
|
|
|
|
.local-box {
|
|
display: flex;
|
|
margin-top: 2px;
|
|
}
|
|
.local-icon {
|
|
flex: 1;
|
|
}
|
|
.local {
|
|
flex: 6;
|
|
margin: 0 10px 0 10px;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.copy-icon {
|
|
flex: 1;
|
|
cursor: pointer;
|
|
flex-direction: row;
|
|
}
|
|
|
|
.token-view {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.token-view span {
|
|
flex: 5;
|
|
white-space: nowrap;
|
|
overflow: hidden;
|
|
text-overflow: ellipsis;
|
|
}
|
|
|
|
.tag-item-on{
|
|
color: white;
|
|
cursor: pointer;
|
|
background-image: linear-gradient(to top, #0ba360 0%, #3cba92 100%) !important;
|
|
}
|
|
.tag-item-off{
|
|
color: white;
|
|
cursor: pointer;
|
|
background-image: linear-gradient(to top, rgba(255, 8, 68, 0.93) 0%, #D54941 100%) !important;
|
|
}
|
|
.browse-icon {
|
|
flex: 2;
|
|
}
|
|
:global(.t-dialog__ctx .t-dialog__position) {
|
|
padding: 48px 10px;
|
|
}
|
|
@media (max-width: 1024px) {
|
|
.setting-box {
|
|
grid-template-columns: 1fr 1fr;
|
|
}
|
|
}
|
|
|
|
@media (max-width: 786px) {
|
|
.setting-box {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
|
|
.card-box {
|
|
margin: 10px 20px 0 20px;
|
|
}
|
|
|
|
.card-none {
|
|
line-height: 400px !important;
|
|
}
|
|
|
|
.dialog-body {
|
|
max-height: 50vh;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
width: 0;
|
|
background: transparent;
|
|
}
|
|
</style>
|
|
<style>
|
|
.setting-card .t-card__title {
|
|
text-align: left !important;
|
|
}
|
|
|
|
.setting-card .t-card__description {
|
|
margin-bottom: 0;
|
|
font-size: 12px;
|
|
}
|
|
|
|
.setting-base-info .t-descriptions__header {
|
|
font-size: 15px;
|
|
margin-bottom: 0;
|
|
}
|
|
|
|
.setting-base-info .t-descriptions__label {
|
|
padding: 0 var(--td-comp-paddingLR-l) !important;
|
|
}
|
|
|
|
.setting-base-info tr > td:last-child {
|
|
text-align: right;
|
|
}
|
|
|
|
.info-coll .t-collapse-panel__wrapper .t-collapse-panel__content {
|
|
padding: var(--td-comp-paddingTB-m) var(--td-comp-paddingLR-l);
|
|
}
|
|
</style>
|